Flutter/Dart中如何围绕参考点实现旋转与缩放?
在Flutter的Canvas中围绕指定参考点旋转/缩放图片
问题描述
我使用CustomPaint组件,传入继承自CustomPainter的对象,在paint()方法中通过Canvas绘制图片。现在需要围绕非原点的参考点(比如Offset(100,200))旋转图片,但按照其他语言的思路(先将参考点移到原点,旋转后移回)实现后,旋转仍围绕图片左上角进行。后续还想实现围绕该参考点的缩放,同样无效。
我尝试过Transform组件可以实现需求,但想知道如何在paint()方法和Canvas中直接实现。
我的错误代码如下:
class SomeWidget extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint(painter: SomePainter()); } } class SomePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { canvas.translate(-100, -200); // 逆时针旋转30° canvas.rotate(-30 / 180 * pi); canvas.translate(100, 200); // 可能需要先对这个向量做sin/cos旋转? canvas.drawImage(image, Offset.zero, Paint()); } }
Transform的可行代码:
Transform( // 我的图片是800x400,因此100,200对应图片的12.5%/50%位置 alignment: Alignment(0.125 * 2 - 1, 0.50 * 2 - 1), transform: Matrix4.rotationZ(-30 / 180 * pi), child: CustomPaint(painter: MyPainter()), ),
解决方案:正确的Canvas变换顺序
Flutter中Canvas的变换是矩阵叠加,执行顺序为从后往前作用(即最后调用的变换会先应用到绘制内容上)。围绕参考点旋转/缩放的正确步骤如下:
- 将画布原点平移到参考点位置
- 执行旋转/缩放操作
- 将画布原点平移回原位置的反向偏移
- 绘制图片
1. 围绕参考点旋转的实现
修正后的paint()方法代码:
class SomePainter extends CustomPainter { final Image image; final Offset pivot = const Offset(100, 200); // 参考点 final double rotationAngle = -30 / 180 * pi; // 逆时针30° SomePainter(this.image); @override void paint(Canvas canvas, Size size) { // 保存画布当前状态(可选,如需后续还原变换) canvas.save(); // 步骤1:将原点移到参考点 canvas.translate(pivot.dx, pivot.dy); // 步骤2:执行旋转 canvas.rotate(rotationAngle); // 步骤3:将原点移回原位置的反向 canvas.translate(-pivot.dx, -pivot.dy); // 绘制图片 canvas.drawImage(image, Offset.zero, Paint()); // 还原画布状态(如果之前调用了save) canvas.restore(); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => oldDelegate is SomePainter && oldDelegate.image != image; }
2. 围绕参考点缩放的实现
缩放逻辑和旋转完全一致,只需替换变换操作:
class SomePainter extends CustomPainter { final Image image; final Offset pivot = const Offset(100, 200); // 参考点 final double scale = 1.5; // 放大1.5倍 SomePainter(this.image); @override void paint(Canvas canvas, Size size) { canvas.save(); // 平移到参考点 canvas.translate(pivot.dx, pivot.dy); // 执行缩放 canvas.scale(scale); // 平移回原位置 canvas.translate(-pivot.dx, -pivot.dy); canvas.drawImage(image, Offset.zero, Paint()); canvas.restore(); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => oldDelegate is SomePainter && oldDelegate.image != image; }
3. 同时旋转+缩放
如需同时执行两种变换,按需求顺序调用即可:
canvas.translate(pivot.dx, pivot.dy); canvas.scale(scale); canvas.rotate(rotationAngle); canvas.translate(-pivot.dx, -pivot.dy);
为什么之前的代码无效?
你之前的变换顺序是:
canvas.translate(-100, -200); canvas.rotate(...); canvas.translate(100, 200);
这个顺序的实际作用是:先将图片向右下平移(100,200),然后旋转,再向左上平移(100,200),最终旋转中心还是图片的左上角。而正确的顺序是先将原点移到参考点,旋转后再移回,这样所有后续绘制内容都会围绕参考点完成变换。
内容的提问来源于stack exchange,提问作者Charly
相关产品推荐
相关产品推荐

