import 'dart:math'; import 'dart:ui' as ui; import 'package:dash_painter/dash_painter.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; /// create by 张风捷特烈 on 2020/11/5 /// contact me by email 1981462002@qq.com /// 说明: class Paper extends StatefulWidget { const Paper({Key? key}) : super(key: key); @override State createState() => _PaperState(); } class _PaperState extends State with SingleTickerProviderStateMixin { Line line = Line(start: const Offset(20, 20), end: const Offset(50, 80)); late AnimationController ctrl; @override void initState() { super.initState(); ctrl = AnimationController( vsync: this, duration: const Duration(seconds: 3), )..addListener(_updateLine); } @override void dispose() { line.dispose(); ctrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: GestureDetector( onTap: () { // line.record(); ctrl.forward(from: 0); }, child: CustomPaint( painter: AnglePainter(line: line // linker: linker ), child: Container( color: Colors.grey.withOpacity(0.1), height: 200, width: 200, ), ), ), ); } void _updateLine() { // print("${ctrl.value * 2 * pi}"); Offset center = line.percent(0.2); line.rotate(ctrl.value * 2 * pi, centre: center); // line.rotate(ctrl.value * 2* pi); } } class AnglePainter extends CustomPainter { final DashPainter dashPainter = const DashPainter(span: 4, step: 4); AnglePainter({required this.line}) : super(repaint: line); final Paint pointPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 1; final Paint helpPaint = Paint() ..style = PaintingStyle.stroke ..color = Colors.lightBlue ..strokeWidth = 1; final TextPainter textPainter = TextPainter( textAlign: TextAlign.center, textDirection: TextDirection.ltr, ); final Line line; @override void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); drawHelp(canvas, size); line.paint(canvas); } void drawHelp(Canvas canvas, Size size) { Path helpPath = Path() ..moveTo(-size.width / 2, 0) ..relativeLineTo(size.width, 0) ..moveTo(0, -size.height / 2) ..relativeLineTo(0, size.height); dashPainter.paint(canvas, helpPath, helpPaint); drawHelpText('0°', canvas, Offset(size.width / 2 - 20, 0)); drawHelpText('p0', canvas, line.start.translate(-20, 0)); drawHelpText('p1', canvas, line.end.translate(-20, 0)); // drawHelpText('p2', canvas, Offset(60, 40).translate(10, 0)); // drawAnchor(canvas, Offset(60, 40)); drawAnchor(canvas, line.percent(0.2)); // drawAnchor(canvas, line.percent(0.5)); // drawAnchor(canvas, line.percent(0.8)); drawHelpText( '角度: ${(line.positiveRad * 180 / pi).toStringAsFixed(2)}°', canvas, Offset( -size.width / 2 + 10, -size.height / 2 + 10, ), ); // canvas.drawArc( // Rect.fromCenter(center: line.start, width: 20, height: 20), // 0, // line.positiveRad, // false, // helpPaint, // ); // canvas.save(); // Offset center = const Offset(60, 60); // canvas.translate(center.dx, center.dy); // canvas.rotate(line.positiveRad); // canvas.translate(-center.dx, -center.dy); // canvas.drawCircle(center, 4, helpPaint); // canvas.drawRect( // Rect.fromCenter(center: center, width: 30, height: 60), helpPaint); // canvas.restore(); } void drawAnchor(Canvas canvas, Offset offset) { canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); } void drawHelpText( String text, Canvas canvas, Offset offset, { Color color = Colors.lightBlue, }) { textPainter.text = TextSpan( text: text, style: TextStyle(fontSize: 12, color: color), ); textPainter.layout(maxWidth: 200); textPainter.paint(canvas, offset); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class Line with ChangeNotifier { Line({ this.start = Offset.zero, this.end = Offset.zero, }); Offset start; Offset end; final Paint pointPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 1; void paint(Canvas canvas) { canvas.drawLine(start, end, pointPaint); drawAnchor(canvas, start); drawAnchor(canvas, end); } double get rad => (end - start).direction; double get positiveRad => rad < 0 ? 2 * pi + rad : rad; double get length => (end - start).distance; void drawAnchor(Canvas canvas, Offset offset) { canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); } double detaRotate = 0; void rotate(double rotate, {Offset? centre}) { detaRotate = rotate - detaRotate; centre = centre ?? start; Line p2p0 = Line(start: centre, end: start); Line p2p1 = Line(start: centre, end: end); p2p0._rotateByStart(detaRotate); p2p1._rotateByStart(detaRotate); start = p2p0.end; end = p2p1.end; detaRotate = rotate; notifyListeners(); } Offset percent(double percent){ return Offset( length*percent*cos(rad), length*percent*sin(rad), )+start; } void _rotateByStart(double rotate) { end = Offset( length * cos(rad + rotate), length * sin(rad + rotate), ) + start; } void tick() { notifyListeners(); } }