Flutter椭圆绘制异常:指定矩形角落为原点时位置偏移
问题分析
你当前的问题出在旋转中心和变换顺序错误:
- 不管指定什么原点,代码都固定绕椭圆中心旋转,不符合你“以外接矩形角落为原点定位并旋转”的需求;
- 初始平移的计算逻辑也不对,导致未正确将指定原点对齐到目标坐标。
修正方案
核心思路是:让椭圆绕你指定的原点旋转,同时保证该原点精准定位到你设置的目标坐标。Canvas的变换是逆序执行的,所以要按“旋转→平移”的逻辑反向写变换代码。
修正后的完整代码:
enum OriginType { bottomLeft, bottomRight, topLeft, topRight, center } class Background extends CustomPainter { final double screenWidth; final double screenHeight; Background({required this.screenWidth, required this.screenHeight}); @override void paint(Canvas canvas, Size size) { final paint = Paint()..style = PaintingStyle.fill; const double degToRad = 3.141592653589793238 / 180; canvas.drawRect(Offset.zero & size, Paint()..color = const Color(0xFFFFFFFF)); // 辅助方法:将模糊半径转为sigma值(Flutter官方推荐) double convertRadiusToSigma(double radius) { return radius * 0.57735 + 0.5; } void drawEllipse( double originXPercent, double originYPercent, double widthPercent, double heightPercent, Color color, double blurSigma, double rotateDegrees, { double opacity = 0.7, OriginType origin = OriginType.bottomRight, }) { paint.color = color.withOpacity(opacity); if (blurSigma > 0) { double sigma = convertRadiusToSigma(blurSigma); paint.maskFilter = MaskFilter.blur(BlurStyle.normal, sigma); } else { paint.maskFilter = null; } // 转换百分比为实际像素值 double originX = size.width * originXPercent / 100; double originY = size.height * originYPercent / 100; double width = size.width * widthPercent / 100; double height = size.height * heightPercent / 100; canvas.save(); // 1. 先平移画布,把目标原点位置(originX, originY)移到坐标系原点 canvas.translate(originX, originY); // 2. 绕原点(也就是我们指定的椭圆外接矩形角落)旋转 canvas.rotate(rotateDegrees * degToRad); // 3. 计算椭圆相对于指定原点的偏移量,平移画布让椭圆正确显示 Offset ellipseOffset; switch (origin) { case OriginType.bottomRight: // 椭圆外接矩形的右下角对齐原点,所以椭圆左上角需要偏移(-width, -height) ellipseOffset = Offset(-width, -height); break; case OriginType.bottomLeft: // 椭圆外接矩形的左下角对齐原点,左上角偏移(0, -height) ellipseOffset = Offset(0, -height); break; case OriginType.topRight: // 椭圆外接矩形的右上角对齐原点,左上角偏移(-width, 0) ellipseOffset = Offset(-width, 0); break; case OriginType.topLeft: // 椭圆外接矩形的左上角对齐原点,无偏移 ellipseOffset = Offset(0, 0); break; case OriginType.center: // 椭圆中心对齐原点,左上角偏移(-width/2, -height/2) ellipseOffset = Offset(-width / 2, -height / 2); break; } canvas.translate(ellipseOffset.dx, ellipseOffset.dy); // 绘制椭圆 canvas.drawOval(Rect.fromLTWH(0, 0, width, height), paint); canvas.restore(); } // 示例:将椭圆外接矩形的bottomRight定位在屏幕中心,旋转135度 drawEllipse(50, 50, 60, 60, const Color(0xFFF00000), 0, 135, origin: OriginType.bottomRight); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return oldDelegate != this; } }
关键修正点
- 参数命名调整:把原来的
centerXPercent/centerYPercent改成originXPercent/originYPercent,更贴合需求,避免混淆; - 变换顺序调整:先平移到目标原点→绕原点旋转→再平移椭圆到对应位置,确保旋转中心是你指定的外接矩形角落;
- 偏移计算逻辑修正:针对每个
OriginType,正确计算椭圆相对于原点的偏移量,保证指定角落精准对齐目标坐标; - 补充了
shouldRepaint方法:CustomPainter必须实现这个方法,否则会有警告。
内容的提问来源于stack exchange,提问作者Mircea
相关产品推荐
相关产品推荐

