封面由 AI 生成,仅作主题示意。
封面由 AI 生成,仅作主题示意。
把同一张商品图用于正方形卡片和竖版内容,直接修改宽高,会让瓶子变胖或变瘦。只保持比例,又可能留下大块空白。这里实现的是第三种选择:保持比例,通过中心裁切填满目标画布。
它是一段浏览器端处理代码,不调用任何生图服务,也不上传文件。Picset 或其他工具导出的候选素材,同样可以作为输入;代码并不假设 Picset 有特定 API 或导出集成。[1]
AI 编辑插图;实际裁切规则以代码为准,不是工具截图。
裁切位置由两个比例决定
设原图宽高为 sw、sh,目标为 tw、th。原图更宽时,保留原高度并截取中间宽度;原图更高时,保留原宽度并截取中间高度。这样得到的源矩形与目标具有相同宽高比,再绘入画布,主体不会因非等比缩放而被拉伸。
但中心裁切并不知道商品在哪里。提手靠边、商品放在画面右侧、文字位于顶部时,它可能把重要部分切掉。因此这个函数适合生成预览候选,不应在没有检查的情况下批量替换线上图片。若要做主体感知,需要另外的标注或检测过程。
处理解码、导出与资源回收
createImageBitmap() 用来解码输入图像,Canvas 的 toBlob() 负责导出。[2][3] 代码同时限制输入体积、解码后像素总量和输出像素总量;这些都是演示策略,不是平台规则,也不是完整的恶意文件防护方案。
function cropRect(sw, sh, tw, th) {
for (const value of [sw, sh, tw, th])
if (!Number.isSafeInteger(value) || value <= 0)
throw new Error('宽高必须为正整数');
if (sw / sh > tw / th) {
const w = sh * tw / th;
return { x: (sw - w) / 2, y: 0, w, h: sh };
}
const h = sw * th / tw;
return { x: 0, y: (sh - h) / 2, w: sw, h };
}
async function exportCrop(file, tw, th) {
cropRect(1, 1, tw, th); // 验证输出宽高
if (tw * th > 16000000) throw new Error('输出画布过大');
if (!file || !Number.isFinite(file.size) ||
file.size > 20 * 1024 * 1024) throw new Error('输入过大或无效');
if (!['image/png', 'image/jpeg', 'image/webp'].includes(file.type))
throw new Error('示例仅接收 PNG、JPEG、WebP');
const bitmap = await createImageBitmap(file);
try {
const { width: sw, height: sh } = bitmap;
if (sw * sh > 40000000) throw new Error('解码后像素过多');
const rect = cropRect(sw, sh, tw, th);
const canvas = document.createElement('canvas');
canvas.width = tw; canvas.height = th;
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('无法创建二维画布');
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(bitmap, rect.x, rect.y, rect.w, rect.h,
0, 0, tw, th);
return await new Promise((resolve, reject) => {
canvas.toBlob(blob => blob ? resolve(blob)
: reject(new Error('导出失败')), 'image/png');
});
} finally { bitmap.close(); }
}
在文件选择事件中取得 File,调用 exportCrop(file, 800, 800) 即可获得正方形 PNG 的 Blob。这里的输出尺寸是示例。返回值应先交给预览组件,再决定下载或进入下一环节。
如果使用 URL.createObjectURL(blob) 创建预览地址,应在图片不再使用或组件卸载时调用 URL.revokeObjectURL(url)。[4] 不要在刚设置图片地址后立即释放;也不要每次重选文件都保留旧地址。上述函数没有创建对象 URL,所以这项管理由调用方完成。
函数通过测试,图片仍要看
配套测试覆盖横图、竖图和同宽高比的裁切矩形,并在本机浏览器中检查 PNG 导出、透明区域与无效文件的失败路径。结果随文章一起交付;它说明这些输入下代码执行情况,不代表在全部浏览器、超大图或任意编码文件下均可靠。
一个需要补上的边界是内存。像素上限在解码以后检查,不能阻止解码阶段本身的内存压力。需要处理大量不可信上传时,还应考虑独立处理进程、服务端限制与更严格的文件检查,不能把浏览器示例当成上传安全模块。
业务预览则重点看主体边缘、文字和配件。图片没有拉伸,只说明几何处理正确;被切掉的杯盖和被截断的参数,仍然会让商品信息不完整。技术检查与内容检查都保留,裁切函数才适合进入商品图片工作流。
参考资料
[1] Picset 产品介绍(国际站提示升级为 Picell AI。)