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

Flutter CustomPainter导出全尺寸图像点位偏移问题排查

问题根源

你遗漏了预览模式下图像居中带来的偏移量处理,此外原有缩放系数的判断逻辑存在边界问题。
具体问题点如下:

  1. 缩放系数计算逻辑缺陷:通过宽高差值判断缩放比例仅适用于图像尺寸大于屏幕的场景,如果图像小于屏幕,差值均为负,会得到错误的缩放比例,直接比较宽高缩放比例的最大值更稳妥。
  2. 缺失居中偏移处理:你在paintImage时指定了alignment: Alignment.center,当缩放后图像的宽高比和预览画布的宽高比不一致时,图像会在预览画布中上下/左右留空,此时你捕获的localPosition是相对于预览画布的坐标,包含了空白边距的偏移,直接乘缩放系数就会导致点位偏移。

修复方案

1. 修正setSizes方法,新增预览偏移量计算

late Offset previewImgOffset; // 新增成员变量,存储预览时图像的居中偏移量
void setSizes() async {
  imgOgSize = Size(image.value!.width.toDouble(), image.value!.height.toDouble());
  double wRatio = imgOgSize!.width / Get.width;
  double hRatio = imgOgSize!.height / Get.height;
  // 直接取宽高比例最大值作为缩放系数,兼容图比屏幕大/小的所有场景
  coef.value = max(wRatio, hRatio);
  // 计算缩放后的图像尺寸
  final scaledImgW = imgOgSize!.width / coef.value;
  final scaledImgH = imgOgSize!.height / coef.value;
  canvasSize = Size(Get.width, Get.height);
  // 计算居中所需的偏移量
  previewImgOffset = Offset(
    (canvasSize!.width - scaledImgW) / 2,
    (canvasSize!.height - scaledImgH) / 2,
  );
  print(coef.value);
}

2. 修正手势点位捕获逻辑,存储原图坐标

GestureDetector(
  onPanDown: (detailData) {
    // 先减去预览偏移得到相对于缩放后图像的坐标,再乘系数得到原图坐标存储
    final relativeX = detailData.localPosition.dx - previewImgOffset.dx;
    final relativeY = detailData.localPosition.dy - previewImgOffset.dy;
    final originalPoint = Offset(relativeX * coef.value, relativeY * coef.value);
    controller.editor.value!.update(originalPoint);
    controller.canvasKey.currentContext!.findRenderObject()!.markNeedsPaint();
  },
  onPanUpdate: (detailData) {
    final relativeX = detailData.localPosition.dx - previewImgOffset.dx;
    final relativeY = detailData.localPosition.dy - previewImgOffset.dy;
    final originalPoint = Offset(relativeX * coef.value, relativeY * coef.value);
    controller.editor.value!.update(originalPoint);
    controller.canvasKey.currentContext!.findRenderObject()!.markNeedsPaint();
  },
  child: CustomPaint(
    key: controller.canvasKey,
    painter: controller.editor.value,
    size: controller.canvasSize!,
  ),
)

3. 修正paint方法,分离预览/导出绘制逻辑

void paint(Canvas canvas, Size size) {
  final imageSize = Size(image.width.toDouble(), image.height.toDouble());
  final src = Offset.zero & imageSize;
  if (fullSize) {
    // 导出模式:1:1绘制原图,直接使用存储的原图坐标
    final dst = Offset.zero & size;
    canvas.drawImageRect(image, src, dst, Paint()..filterQuality = FilterQuality.high);
    for (Offset offset in points) {
      canvas.drawCircle(offset, 10 * coef.value, painter);
    }
  } else {
    // 预览模式:绘制缩放后居中的图像,把原图坐标转为预览坐标
    final scaledImgW = imageSize.width / coef;
    final scaledImgH = imageSize.height / coef;
    final dst = previewImgOffset & Size(scaledImgW, scaledImgH);
    canvas.drawImageRect(image, src, dst, Paint()..filterQuality = FilterQuality.high);
    for (Offset offset in points) {
      final previewOffset = Offset(
        offset.dx / coef + previewImgOffset.dx,
        offset.dy / coef + previewImgOffset.dy,
      );
      canvas.drawCircle(previewOffset, 10, painter);
    }
  }
}

内容的提问来源于stack exchange,提问作者narrei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 19:57:03