Flutter CustomPainter导出全尺寸图像点位偏移问题排查
问题根源
你遗漏了预览模式下图像居中带来的偏移量处理,此外原有缩放系数的判断逻辑存在边界问题。
具体问题点如下:
- 缩放系数计算逻辑缺陷:通过宽高差值判断缩放比例仅适用于图像尺寸大于屏幕的场景,如果图像小于屏幕,差值均为负,会得到错误的缩放比例,直接比较宽高缩放比例的最大值更稳妥。
- 缺失居中偏移处理:你在
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
相关产品推荐
相关产品推荐

