图片裁剪在页面上看起来只是一个矩形框,但真正实现时至少有三套坐标:原图像素坐标、容器显示坐标、用户拖拽产生的视口坐标。只在 CSS 层调整 object-position,再把同一组数直接提交给后端,最容易出现"预览正常、导出偏移"。
这篇用一个固定思路解决问题:先算出图片在容器里的真实显示矩形,再把裁剪框从显示坐标反推回原图像素坐标。object-fit: cover 只负责显示策略,不等于裁剪结果已经生成。
一、先区分三套坐标
假设原图尺寸是 W × H,容器尺寸是 Cw × Ch。使用 object-fit: cover 时,缩放比例是 max(Cw / W, Ch / H),显示后的图片尺寸为 Dw × Dh。多出来的部分会被裁掉:水平溢出为 Dw - Cw,垂直溢出为 Dh - Ch。
用户看到的裁剪框坐标 (x, y, w, h) 属于容器坐标;后端需要的是原图坐标。因此不能只把 x、y 传过去,必须先减去图片在容器中的偏移,再除以缩放比例。
二、object-position 为什么会影响结果
默认的 object-position: 50% 50% 会把水平和垂直溢出各分一半。如果改成 left top,所有溢出都会落在右侧和底部。相同的裁剪框,在这两种位置下对应的原图区域完全不同。
一个常见错误是:拖动预览时只修改 CSS 的 object-position,导出时却仍按居中偏移计算。另一个错误是把百分比当成原图像素,导致小图和大图的误差不一致。解决办法是把"显示矩形"作为唯一中间结果,预览和导出都消费它。
三、用函数把显示矩形算出来
js
function getCoverRect(imageW, imageH, boxW, boxH, positionX = 0.5, positionY = 0.5) {
const scale = Math.max(boxW / imageW, boxH / imageH);
const displayW = imageW * scale;
const displayH = imageH * scale;
const offsetX = (boxW - displayW) * positionX;
const offsetY = (boxH - displayH) * positionY;
return { scale, displayW, displayH, offsetX, offsetY };
}
function viewToImage(point, rect) {
return {
x: (point.x - rect.offsetX) / rect.scale,
y: (point.y - rect.offsetY) / rect.scale
};
}
这里的 positionX 和 positionY 使用 0 到 1 的数值:0 表示左或上,0.5 表示居中,1 表示右或下。实际项目中要把裁剪框的四个角都换算一次,再做边界裁切和取整。
四、拖拽时不要直接改原图坐标
拖拽事件的坐标来自浏览器视口,通常还要减去容器的 getBoundingClientRect().left/top。先得到容器坐标,再调用 viewToImage。如果在拖动过程中同时改变容器大小、设备像素比或缩放比例,应重新计算显示矩形,而不是复用上一次的偏移。
建议把状态拆成三层:source 保存原图尺寸,viewport 保存容器和 object-position,selection 保存容器中的裁剪框。渲染时由三层状态计算结果,避免在多个事件里互相写入派生值。这样窗口旋转、响应式布局和撤销操作都更容易处理。
五、验收时重点看四个边界
第一,宽图放入竖向容器,检查左右溢出;第二,竖图放入横向容器,检查上下溢出;第三,把 object-position 从 0 调到 1,确认裁剪区域随之完整移动;第四,把显示容器缩放到一半,再导出同一选区,原图坐标应保持一致。
还要测设备像素比。CSS 像素和位图像素不是一回事,导出 Canvas 时要明确目标宽高,不能把 devicePixelRatio 隐式乘两次。所有边界坐标最后统一 Math.round,并限制在 [0, imageSize] 范围内。
六、一个实用的排查顺序
如果"预览和导出不一致",先打印原图尺寸、容器尺寸、scale、offsetX、offsetY;再确认拖拽坐标是否已经减去容器左上角;然后检查 object-position 是否和导出端使用同一组参数;最后才排查 Canvas 绘制和压缩格式。这样能快速判断是布局问题、坐标换算问题,还是导出问题。
结论:object-fit 解决的是"怎么显示",坐标换算解决的是"到底截取哪一块"。把显示矩形作为中间层,并用真实像素做一次反推,裁剪、缩放和导出才能共用同一套逻辑。
AI辅助创作:本文由人工审阅,并依据公开前端与图像处理资料整理。