Flutter自定義widget---可拖拽二階貝塞爾曲線

一、先放上一張靜態(tài)圖


可拖拽二階貝塞爾曲線.png

可以拖動起點(diǎn),控制點(diǎn)和終點(diǎn)查看二階貝塞爾曲線。

二、實(shí)現(xiàn)思路
二階貝塞爾曲線就是三個點(diǎn),所以當(dāng)點(diǎn)擊不超過三個點(diǎn)的時候只需要繪制點(diǎn)。否則就繪制二階貝塞爾曲線和點(diǎn)的連線。當(dāng)拖動其中一個點(diǎn)的時候重新繪制。所以重點(diǎn)是如何觸發(fā)重繪。
畫板的重繪需要一個Listenable對象。ChangeNotifier是一個實(shí)現(xiàn)了Lisenable的類,在數(shù)據(jù)變動的時候可以通過notifyListeners()來刷新界面。

三、代碼實(shí)現(xiàn)

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);
  }

  ///判斷是否在某點(diǎn)半徑范圍內(nèi)
  bool judgeCircleArea(Offset src, Offset dst, double r) =>
      (src - dst).distance <= r;

  ///判斷那個點(diǎn)被選中
  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);

  ///因?yàn)楫嫴计揭屏?,所以三個點(diǎn)也需要平移
    pos = repaint.points.map((e) => e.translate(-size.width / 2,- size.height / 2)).toList();

    ///如果點(diǎn)數(shù)少于三個就繪制點(diǎn)  如果大于三個就繪制貝塞爾曲線,繪制輔助線
    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);
    }

  }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容