補充“拖拽調整曲線控制點畫粽子”的完整程式碼
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
class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State
class _MyHomePageState extends State
class Test extends StatefulWidget { const Test({Key? key}) : super(key: key);
@override _TestState createState() { return _TestState(); } }
class _TestState extends State
Offset? post;
List
@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
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
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
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
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上啊。 是吧。