Flutter自定义widget---可拖拽二阶贝塞尔曲线

一、先放上一张静态图


可拖拽二阶贝塞尔曲线.png

可以拖动起点,控制点和终点查看二阶贝塞尔曲线。

二、实现思路
二阶贝塞尔曲线就是三个点,所以当点击不超过三个点的时候只需要绘制点。否则就绘制二阶贝塞尔曲线和点的连线。当拖动其中一个点的时候重新绘制。所以重点是如何触发重绘。
画板的重绘需要一个Listenable对象。ChangeNotifier是一个实现了Lisenable的类,在数据变动的时候可以通过notifyListeners()来刷新界面。

三、代码实现

import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/canvas/canvas.dart';
import 'package:flutter_app/path/path.dart';

class Path2 extends StatefulWidget {
  @override
  State createState() {
    return Path2State();
  }
}

class Path2State extends State {
  TouchInfo touchInfo = TouchInfo();

  @override
  void dispose() {
    super.dispose();
    touchInfo.dispose();
  }

  @override
  void initState() {
    super.initState();
    initPoints();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: GestureDetector(
        onPanDown: _onPanDown,
        onPanUpdate: _onPanUpdate,
        child: CustomPaint(
          painter: Path2CustomPainter(repaint: touchInfo),
        ),
      ),
    );
  }

  void _onPanDown(DragDownDetails details) {
    if (touchInfo.points.length < 3) {
      touchInfo.addPoint(details.localPosition);
    } else {
      ///绘制曲线
      judgeZone(details.localPosition);
    }
  }

  void _onPanUpdate(DragUpdateDetails details) {
    judgeZone(details.localPosition, update: true);
  }

  ///判断是否在某点半径范围内
  bool judgeCircleArea(Offset src, Offset dst, double r) =>
      (src - dst).distance <= r;

  ///判断那个点被选中
  void judgeZone(Offset src, {bool update = false}) {
    for (int i = 0; i < touchInfo.points.length; i++) {
      if (judgeCircleArea(src, touchInfo.points[i], 15)) {
        touchInfo.selectIndex = i;
        if (update) {
          touchInfo.updatePoint(i, src);
        }
      }
    }
  }
}
class TouchInfo extends ChangeNotifier {
  List _points = [];
  int _selectIndex = -1;

  int get selectIndex => _selectIndex;

  List get points => _points;

  set selectIndex(int value) {
    assert(value != null);
    if (_selectIndex == value) return;
    _selectIndex = value;
    notifyListeners();
  }

  void addPoint(Offset offset) {
    points.add(offset);
    notifyListeners();
  }

  void updatePoint(int index, Offset point) {
    points[index] = point;
    notifyListeners();
  }

  Offset get selectPoint => _selectIndex == -1 ? null : _points[_selectIndex];
}

class Path2CustomPainter extends CustomPainter {
  final TouchInfo repaint;

  List pos;

  Path2CustomPainter({this.repaint}):super(repaint:repaint);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width / 2, size.height / 2);
//     path(canvas);
    drawCoordinate(canvas, size);
    drawGirdLine(canvas, size);
////    quadraticBezierTo(canvas);

  ///因为画布平移了,所以三个点也需要平移
    pos = repaint.points.map((e) => e.translate(-size.width / 2,- size.height / 2)).toList();

    ///如果点数少于三个就绘制点  如果大于三个就绘制贝塞尔曲线,绘制辅助线
    if(pos.length < 3){
      canvas.drawPoints(PointMode.points, pos, Paint()..color = Colors.purple
      ..strokeWidth = 8..strokeCap = StrokeCap.round);
    }else{
      Path path = Path();
      path.moveTo(pos[0].dx, pos[0].dy);
      path.quadraticBezierTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy);
      canvas.drawPath(path, Paint()..color = Colors.purple..style = PaintingStyle.stroke
      ..strokeWidth = 2);
      
      ///画线
      canvas.drawPoints(PointMode.points, pos, Paint()..color = Colors.purple
        ..strokeWidth = 8..strokeCap = StrokeCap.round);
      canvas.drawPoints(PointMode.polygon, pos, Paint()..color = Colors.purple);
    }

  }

你可能感兴趣的:(Flutter自定义widget---可拖拽二阶贝塞尔曲线)