裁剪即美化,关键在于将裁剪区域作为独立画布处理滤镜:先用drawimage按裁剪坐标绘制到等尺寸临时canvas,再用getimagedata获取纯净rgba数据;滤镜函数遍历步长为4,灰度用加权公式并钳位,最后用putimagedata写回并导出。

裁剪即美化,关键不是先裁再滤,而是把裁剪区域当作独立画布来处理滤镜——这样能避免全图计算浪费,也保证滤镜只作用在用户真正要保留的部分。
裁剪区域单独提取为新图像数据
不要在原图上直接 drawImage + clip,那样滤镜仍会遍历整张图。正确做法是:
- 用 drawImage 把原始图片按裁剪坐标和尺寸绘制到一个临时 canvas 上,宽高严格等于裁剪框大小(比如
tempCanvas.width = cropWidth; tempCanvas.height = cropHeight;) - 调用 getImageData(0, 0, cropWidth, cropHeight) 获取纯裁剪区的像素数据,这才是后续滤镜操作的输入源
- 这个 ImageData.data 就是干净、紧凑、无冗余的 RGBA 数组,长度固定为
cropWidth × cropHeight × 4
滤镜只跑在裁剪后的像素上
把滤镜逻辑封装成纯函数(如 grayscale(data, options)),传入上面拿到的 ImageData.data,直接修改并返回。重点注意:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 遍历步长必须是 4,每次处理 R/G/B/A 四个字节;Alpha 通道默认不改,除非做透明度滤镜
- 灰度推荐加权公式:0.299×R + 0.587×G + 0.114×B,比简单平均更准
- 运算后值可能越界(如亮度+50导致 >255),需用
Math.max(0, Math.min(255, val))钳位
结果写回并导出
滤镜处理完的数据,需要重新构造成 ImageData 对象,再绘制到最终显示 canvas 或导出:
- 用 new ImageData(data, cropWidth, cropHeight) 创建新对象(data 是已处理的 Uint8ClampedArray)
- 用 putImageData 写入目标 canvas 的指定位置(通常是 0, 0)
- 如需下载,调用
canvas.toDataURL('image/png')—— 此时 canvas 内容就是裁剪+滤镜完成的结果,无多余像素
跨域与时机不能错
整个流程卡住最常见的不是算法,而是基础准备:
-
图片必须设
crossOrigin="anonymous",否则 getImageData 报 SecurityError - drawImage 必须在 img.onload 后执行,且确保图片 naturalWidth/naturalHeight 已就绪
- 临时 canvas 宽高务必用
img.naturalWidth和img.naturalHeight计算,别用 CSS 缩放后的 offsetWidth/Height










