美文网首页Android学习记录
Flutter自定义widget---可拖拽二阶贝塞尔曲线

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

作者: 旺仔_100 | 来源:发表于2020-11-26 13:50 被阅读0次

    一、先放上一张静态图


    可拖拽二阶贝塞尔曲线.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<StatefulWidget> createState() {
        return Path2State();
      }
    }
    
    class Path2State extends State<Path2> {
      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<Offset> _points = [];
      int _selectIndex = -1;
    
      int get selectIndex => _selectIndex;
    
      List<Offset> 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<Offset> 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---可拖拽二阶贝塞尔曲线

        本文链接:https://www.haomeiwen.com/subject/ompaiktx.html