补充“拖拽调整曲线控制点画粽子”的完整代码

demo代码不全怎么看效果

不看效果,怎么确认可正常使用

不确认能使用,怎么在以后回头再利用

起因

看了@岛上码农的文章《拖拽调整曲线控制点画粽子》,感觉很有意思。实际做项目中,除了写写界面,就是调用下api,用到自己画图的很少。 可以看了文章后,并自己敲了一遍代码后,发现demo的代码只有一部分,不能实现效果,这怎么能接受。一定要实现这个效果,然后开始自己脑补代码。 完成 一顿操作后,终于实现了demo的效果。想着把代码在评论区分享下,然后提示“评论内容超出字数”。这这这。。。

完整代码

所以只能将脑补的demo记录在此:

import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_snippet/Common/my_colors.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: MyColors.white,
      ),
      debugShowCheckedModeBanner: false,
      home: const MyHomePage(title: "玩哈哈"),
      // 国际化配置 __START__
      localeListResolutionCallback:
          (List<Locale>? locals, Iterable<Locale>? supportedLocales) {
        return const Locale('zh');
      },
      localeResolutionCallback:
          (Locale? locale, Iterable<Locale>? supportedLocales) {
        return const Locale("zh");
      },
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate
      ],
      supportedLocales: const [
        Locale('zh', 'CH'),
        Locale('en', 'US'),
      ],
      // 国际化配置 __END__
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("花花世界"),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_ios),
          onPressed: () {},
        ),
      ),
      body: const Test(),
    );
  }
}

class Test extends StatefulWidget {
  const Test({Key? key}) : super(key: key);

  @override
  _TestState createState() {
    return _TestState();
  }
}

class _TestState extends State<Test>
    with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
  late Animation<double> animation;
  late AnimationController controller;

  Offset? post;
  List<Offset> points = <Offset>[];
  int indexOfPointToMove = -1;
  bool drawing = true;

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

    controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 2));
    animation = Tween<double>(begin: 0, end: 360).animate(controller)
      ..addListener(() {
        setState(() {});
      });
    // controller.repeat();
  }

  @override
  void dispose() {
    controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    debugPrint("post rebuild!");

    return Stack(
      children: [
        Listener(
          onPointerUp: (event) {
            if (drawing) {
              points.add(event.localPosition);

              setState(() {});
            }
          },
          onPointerMove: (event) {
            if (!drawing && indexOfPointToMove != -1) {
              points[indexOfPointToMove] = event.localPosition;
              setState(() {});
            }
          },
          onPointerDown: (event) {
            if (!drawing) {
              indexOfPointToMove = checkPointToMove(event.localPosition);

              setState(() {});
            }
          },
          behavior: HitTestBehavior.opaque,
          child: CustomPaint(
            key: UniqueKey(),
            foregroundPainter: TestPaint(points),
            child: Container(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              color: const Color(0xFFF5F5F5),
            ),
          ),
        ),
        ElevatedButton(
          onPressed: () {
            if (points.isNotEmpty) {
              points.removeLast();
              setState(() {});
            }
          },
          child: const Icon(
            Icons.backspace,
            color: Colors.blue,
          ),
        ),
        Align(
          alignment: Alignment.topRight,
          child: ElevatedButton(
            onPressed: () {
              setState(() {
                drawing = !drawing;
              });
            },
            child: drawing
                ? const Icon(
                    Icons.edit,
                    color: Colors.cyan,
                  )
                : const Icon(
                    Icons.done,
                    color: Colors.cyan,
                  ),
          ),
        )
      ],
    );
  }

  @override
  bool get wantKeepAlive => true;

  int checkPointToMove(Offset pressedPoint) {
    if (points.isEmpty) {
      return -1;
    }

    var pointsToCheck = <Offset>[];
    const maxDistance = 10.0;
    for (Offset p in points) {
      if ((p.dx - pressedPoint.dx).abs() < maxDistance &&
          (p.dy - pressedPoint.dy).abs() < maxDistance) {
        pointsToCheck.add(p);
      }
    }

    if (pointsToCheck.isEmpty) {
      return -1;
    } else if (pointsToCheck.length == 1) {
      return points.indexOf(pointsToCheck[0]);
    } else {
      Offset point = pointsToCheck.first;
      var distance = distanceBetween(pointsToCheck.first, pressedPoint);
      for (int i = 1; i < pointsToCheck.length; i++) {
        var newDistance = distanceBetween(pointsToCheck[i], pressedPoint);

        if (newDistance < distance) {
          point = pointsToCheck[i];
          distance = newDistance;
        }
      }

      return points.indexOf(point);
    }
  }

  double distanceBetween(Offset first, Offset second) {
    double width = (first.dx - second.dx).abs();
    double height = (first.dy - second.dy).abs();

    return sqrt(width * width + height * height);
  }
}

class TestPaint extends CustomPainter {
  final List<Offset> points;

  TestPaint(this.points, {Key? key});

  @override
  void paint(Canvas canvas, Size size) {
    debugPrint("$size");

    canvas.drawColor(const Color(0xFFF1F1F1), BlendMode.color);
    var paint = Paint()
      ..color = const Color(0xFFE53020)
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;
    for (var point in points) {
      canvas.drawCircle(point, 2.0, paint);
    }
    paint.color = const Color(0xFF2480F0);
    drawCurves(canvas, paint, points);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }

  void drawCurves(Canvas canvas, Paint paint, List<Offset> points) {
    if (points.length <= 1) {
      return;
    }

    if (points.length == 2) {
      canvas.drawLine(points[0], points[1], paint);
      return;
    }

    if (points.length == 3) {
      _drawTwoOrderBezierCurves(canvas, paint, points);
      return;
    }

    if (points.length == 4) {
      _drawThreeOrderBezierCurves(canvas, paint, points);
      return;
    }

    var subPoints = points.sublist(0, 4);
    drawCurves(canvas, paint, subPoints);
    drawCurves(canvas, paint, points.sublist(3));
  }

  void _drawThreeOrderBezierCurves(
      Canvas canvas, Paint paint, List<Offset> points) {
    assert(points.length == 4);
    var yGap = 60.0, path = Path();
    path.moveTo(points.first.dx, points.first.dy);

    for (var t = 1; t <= 100; t++) {
      var curvePoint = get3OrderBezierPoint(
          points[0], points[1], points[2], points[3], t / 100.0);
      path.lineTo(curvePoint.dx, curvePoint.dy);
    }

    canvas.drawPath(path, paint);
  }

  void _drawTwoOrderBezierCurves(
      Canvas canvas, Paint paint, List<Offset> points) {
    assert(points.length == 3);
    var path = Path();
    path.moveTo(points.first.dx, points.first.dy);
    for (var t = 1; t <= 100; t++) {
      var curvePoint =
          get2OrderBezierPoint(points[0], points[1], points[2], t / 100.0);
      path.lineTo(curvePoint.dx, curvePoint.dy);
    }

    canvas.drawPath(path, paint);
  }

  Offset get3OrderBezierPoint(
      Offset p1, Offset p2, Offset p3, Offset p4, double t) {
    var x = (1 - t) * (1 - t) * (1 - t) * p1.dx +
        3 * t * (1 - t) * (1 - t) * p2.dx +
        3 * t * t * (1 - t) * p3.dx +
        t * t * t * p4.dx;
    var y = (1 - t) * (1 - t) * (1 - t) * p1.dy +
        3 * t * (1 - t) * (1 - t) * p2.dy +
        3 * t * t * (1 - t) * p3.dy +
        t * t * t * p4.dy;

    return Offset(x, y);
  }

  Offset get2OrderBezierPoint(Offset p1, Offset p2, Offset p3, double t) {
    var x = (1 - t) * (1 - t) * p1.dx + 2 * t * (1 - t) * p2.dx + t * t * p3.dx;
    var y = (1 - t) * (1 - t) * p1.dy + 2 * t * (1 - t) * p2.dy + t * t * p3.dy;

    return Offset(x, y);
  }
}
复制代码

结尾

还是希望demo有完整的代码,可以放到gitHub上啊。 是吧。

猜你喜欢

转载自juejin.im/post/7106012010923425829