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

HTML5 Canvas图片旋转与定位异常:高分辨率导出问题

问题分析与解决方案

你的导出异常核心问题是Canvas变换顺序与DOM元素的变换逻辑不匹配,同时错误处理了旋转中心与元素定位的对应关系,导致旋转后元素位置偏移。以下是具体修正方案:

核心问题点

  1. 无效设置layer.image.style.top/left:drawImage直接读取图片元素原始尺寸,不会应用其style定位,这部分代码完全无用。
  2. 变换顺序错误:当前先平移到元素左上角再旋转,最后以中心偏移绘制,和DOM中基于transformOrigin的旋转逻辑完全不符。
  3. 旋转中心计算未结合原元素定位:DOM元素的旋转基于自身transformOrigin,需要将旋转中心的偏移量纳入平移计算。

修正后的代码

// Props
let self = this
let highRes_canvas = []
let mergedCanvas = null
let resolution = {}

if (format === '1-1')
    resolution = { width: 1920, height: 1920 }
else if (format === '16-9')
    resolution = { width: 1920, height: 1080 }
else if (format === '9-16')
    resolution = { width: 1080, height: 1920 }
else
    resolution = { width: this.el_designer.offsetWidth, height: this.el_designer.offsetHeight }

let ratio = resolution.width / this.el_designer.offsetWidth
exportCanvas = document.createElement('canvas')
exportCanvas.width = resolution.width
exportCanvas.height = resolution.height
let ctx = exportCanvas.getContext('2d')

await loadImages() // 确保所有图层图片加载完成

for (const layer of layers) {
    const top = parseFloat(layer.el.style.top) || 0;
    const left = parseFloat(layer.el.style.left) || 0;
    const originalWidth = parseFloat(layer.el.width);
    const originalHeight = parseFloat(layer.el.height);
    const width = originalWidth * ratio;
    const height = originalHeight * ratio;

    if ((layer.type === 'image' || layer.type === 'shape') && layer.image) {
        ctx.save()
        
        // 1. 解析原元素的transformOrigin(兼容百分比/像素单位)
        const [originXStr, originYStr] = layer.el.style.transformOrigin.split(' ');
        let originX, originY;
        if (originXStr.includes('%')) {
            originX = (parseFloat(originXStr) / 100) * originalWidth;
        } else {
            originX = parseFloat(originXStr);
        }
        if (originYStr.includes('%')) {
            originY = (parseFloat(originYStr) / 100) * originalHeight;
        } else {
            originY = parseFloat(originYStr);
        }
        // 转换为高分辨率下的旋转中心偏移量(相对于元素左上角)
        const offsetX = originX * ratio;
        const offsetY = originY * ratio;

        // 2. 计算高分辨率下元素左上角的坐标
        const elementLeft = left * ratio;
        const elementTop = top * ratio;

        // 3. 平移到旋转中心位置(Canvas变换逆序应用,先做平移)
        ctx.translate(elementLeft + offsetX, elementTop + offsetY);
        
        // 4. 应用旋转
        const rotationAngle = await getRotationAngle(layer.el.style.transform); // 确保返回弧度值
        ctx.rotate(rotationAngle);

        // 5. 绘制图像:以旋转中心为原点,偏移回元素左上角位置
        ctx.drawImage(layer.image, -offsetX, -offsetY, width, height);

        ctx.restore();
    } else if (layer.type === "text") { 
        // 文本图层可复用上述变换逻辑处理旋转定位
    }
}

关键修正说明

  • 移除无效代码:删除layer.image.style.top/left设置,drawImage不会读取这些样式。
  • 正确解析transformOrigin:区分百分比和像素单位,转换为原元素实际偏移量后,再乘以分辨率比例得到高分辨率下的偏移值。
  • 匹配DOM变换逻辑:Canvas变换顺序为平移到旋转中心 → 旋转 → 绘制时偏移回元素左上角,和DOM中transformOrigin的作用逻辑完全一致,确保旋转后位置与浏览器中一致。
  • 明确坐标对应:所有定位值基于元素左上角,结合旋转中心偏移计算,避免旋转导致的位置偏移。

额外验证点

  1. 确认getRotationAngle函数正确将DOM的rotate(XXdeg)转换为弧度值(示例:return (parseFloat(angleStr) * Math.PI) / 180;)。
  2. 若图层有缩放等其他变换,需在旋转前同步应用对应的缩放逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:59:51