You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:55:55