使用方法
- 点「选择本地图片」挑一张图,PNG、JPEG、WebP、GIF、BMP 都能读。图片只在你自己的浏览器里解码,没有任何上传请求。
- 载入后结果区显示原始像素宽高(示例源图 4000×3000),四个选区框会被自动填成一块常规取景,然后你在这个基础上微调。
- 「选区 X」和「选区 Y」是这块矩形左上角在源图里的坐标。图像坐标系原点是左上角,X 向右为正、Y 向下为正,示例是 1200 和 1050。
- 「选区宽」和「选区高」是矩形本身的尺寸,示例 1600 和 900,也就是默认那一档 16:9。
- 「比例约束」选 1:1、16:9、4:3、3:2、9:16 时,改宽会自动算出高、改高会自动算出宽;选「自由」则两个方向各填各的。
- 点「裁剪并导出 PNG」,浏览器把选区那一块矩形原样搬到新画布上并触发下载,文件名的形式是 crop-1200x1050-1600x900.png。
这个图片裁剪工具能解决什么
一是改构图不改画质。证件照要方图、封面要 16:9、商品图要固定比例,实际要做的只是从原图里框出一块矩形,剩下的像素原封不动。因为不重采样,画面不会变糊,也不会出现缩放带来的细节丢失。
二是把「凭感觉拖框」变成可复现的四个数。图片裁剪工具把操作拆成 X、Y、宽、高:要做一批同样取景的图,把这四个数抄下来重复用即可,不需要凭手感对齐。对照表里 12 组常用选区就是照这个思路列的。
三是精确到像素地取局部。扫描件里只留某一栏、长图里只取一段、海报里只截一个小图标,都要求边界不差一像素。用坐标框选比用鼠标拖拽稳定得多。
四是顺手统一长宽比。素材来自手机、相机、截图拼在一起时比例五花八门,勾上 16:9 或 1:1,改一个数字另一边自动跟随,出来的图直接能排进同一套版式。
本页的图片裁剪只改画面范围:不旋转、不翻转、不加水印、不调色,也不做批量队列,一次一张。它和「图片尺寸调整」是两件事——那一页改的是整张图的像素总量,本页改的是保留原图的哪一块,输出尺寸由选区决定而不是由你填目标尺寸。
裁剪原理:drawImage 九参数只取一块矩形
裁剪落在 CanvasRenderingContext2D.drawImage() 的九参数重载上。签名是 drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh):前四个 sx, sy, sw, sh 在源图上框出一个矩形,后四个 dx, dy, dw, dh 把这块矩形画到目标画布的矩形里。裁剪的全部秘密就在这两组参数的配合上——本页固定 dx=0, dy=0,并且让 dw=sw、dh=sh,于是源图矩形与目标矩形尺寸完全相等,一个输出像素只对应一个源像素,浏览器不需要做任何插值。这就是「只裁不拉伸」的字面含义:缩放比恒等于 1.00。
反过来说,只要 dw ≠ sw,裁剪就变成了缩放。那是「图片尺寸调整」要解决的问题,用本页做反而会得到一张模糊的图。所以本页把 target 矩形锁死,只让用户控制源矩形。
坐标系沿用 canvas 的约定:原点在左上角。sx 向右增加、sy 向下增加,单位是像素而不是 CSS 像素或百分比,所以缩放浏览器窗口、切换系统缩放比例都不会改变结果。四个数都必须是整数,小数会被拒绝。
比例约束是加在四个数之上的锁。选 16:9 时,宽高满足 sh = round(sw × 9 / 16);选 1:1 时 sh = sw;选 4:3 时 sh = round(sw × 3 / 4)。示例宽度 1600 对应的三个高度分别是 900、1600、1200,都能整除;而 3:2 时 round(1600 × 2 / 3) = 1067,不是整数倍的比就一定会取整,实际比例会偏离理论值一点点(1600:1067 ≈ 1.4995,而不是 1.5),这是像素只能是整数的必然结果。
EXIF 方向会直接影响裁剪坐标。手机竖拍的照片,像素数据往往是横的,方向记录在 EXIF 的 Orientation 字段里。如果按原始像素去取 (sx, sy),你框的位置会和屏幕上看到的方位对不上,导出后还会躺倒。本页优先用 createImageBitmap(blob, { imageOrientation: 'from-image' }),让解码器先把像素摆正,再去算选区坐标;旧浏览器才退回 Image 元素。
越界不会报错,只会静默出问题。当 sx + sw 超过源图宽度时,超出画布范围的部分不会抛异常,而是被当作透明处理——你得到的图上会多出一条空白或黑边,却看不出哪里错了。所以本页在导出前先算 sx + sw 与源图宽、sy + sh 与源图高的关系,越界时把超出的像素数直接写进提示。
完整示例(数值仅作演示)
示例条件与页面打开时的默认值完全一致:源图 4000×3000、选区起点 (1200, 1050)、选区 1600×900、比例 16:9。
- 源图 4000×3000 = 12,000,000 像素,RGBA 缓冲 12,000,000 × 4 = 48,000,000 字节 = 45.78 MiB;
- 选区右边界 1200 + 1600 = 2800,小于 4000;下边界 1050 + 900 = 1950,小于 3000,都不越界;
- 输出 1600×900 = 1,440,000 像素,RGBA 缓冲 5.49 MiB,占源图面积的 12.00%;
- 因为
dw=sw=1600、dh=sh=900,缩放比是 1.00,1,440,000 个像素被 1:1 搬运; - 换成 1:1:选区 (1500, 1000) 取 1000×1000 = 1,000,000 像素、RGBA 3.81 MiB、占源图 8.33%;
- 换成 4:3:选区 (1200, 900) 取 1600×1200 = 1,920,000 像素、RGBA 7.32 MiB、占源图 16.00%。
再看一条边界示例:把「选区 X」填 3600、「选区宽」填 800,右边界就是 4400,超过源图宽 4000 整整 400 px;本页会拦下来并写明超了多少,而不是给你一张右边发虚的图。
选区与输出对照表
下表固定源图 4000×3000,列出 12 组常用选区及其输出像素与 RGBA 缓冲。RGBA 缓冲 = 宽 × 高 × 4 字节,是这块位图在内存里的真实占用,和文件体积不是一回事。可以看出输出像素与选区尺寸严格相等,从不因为比例约束而改变。
| 选区起点 (X, Y) | 选区尺寸 | 比例 | 输出像素 | RGBA 缓冲 | 占源图 |
|---|---|---|---|---|---|
| (1200, 1050) | 1600×900 | 16:9 | 1,440,000 | 5.49 MiB | 12.00% |
| (1500, 1000) | 1000×1000 | 1:1 | 1,000,000 | 3.81 MiB | 8.33% |
| (1200, 900) | 1600×1200 | 4:3 | 1,920,000 | 7.32 MiB | 16.00% |
| (500, 500) | 3000×2000 | 3:2 | 6,000,000 | 22.89 MiB | 50.00% |
| (1460, 540) | 1080×1920 | 9:16 | 2,073,600 | 7.91 MiB | 17.28% |
| (1000, 750) | 2000×1500 | 4:3 | 3,000,000 | 11.44 MiB | 25.00% |
| (976, 924) | 2048×1152 | 16:9 | 2,359,296 | 9.00 MiB | 19.66% |
| (200, 200) | 800×800 | 1:1 | 640,000 | 2.44 MiB | 5.33% |
| (800, 700) | 2400×1600 | 3:2 | 3,840,000 | 14.65 MiB | 32.00% |
| (1400, 700) | 1200×1600 | 3:4 | 1,920,000 | 7.32 MiB | 16.00% |
| (80, 420) | 3840×2160 | 16:9 | 8,294,400 | 31.64 MiB | 69.12% |
| (400, 300) | 3200×2400 | 4:3 | 7,680,000 | 29.30 MiB | 64.00% |
点「加载更多选区」再补 6 行(含整图不裁、2000×2000 方图、2560×1440 等);点「导出 CSV」把当前可见的整张表存成带 BOM 的 CSV,Excel 打开不乱码;点「复制对照表」复制成制表符分隔的文本,可直接粘进表格软件。
边界与精度说明
- 选区 X / Y 允许 0~65535,宽 / 高允许 1~65535,都只收整数。宽或高为 0 时画布不存在,浏览器会直接抛
IndexSizeError;小数会被拒绝,因为源图上不存在半个像素。 - 选区必须完整落在源图内。
X + 宽不能超过源图宽,Y + 高不能超过源图高。超出时 drawImage 不报错,超出部分按透明处理,你只会看到一条凭空多出来的空白边。 - 画布还有两条硬上限:单边 65535 px、总面积 268435456 px(约 16384×16384)。即使 X、Y 都为 0,选区 16500×16400 = 270,600,000 像素也会超限,本页会拦下来并写明超了多少。
- 裁剪不改变像素密度,也不提升清晰度。输出 1600×900 就是 1600×900 个原样像素;把它放到 Retina 屏或印刷时看到的那点模糊,是原图本身的分辨率决定的,不是裁剪造成的。
- 比例约束会取整。像 3:2 这种不能整除的比例,宽度 1600 算出的高度是 1067 而不是 1066.67,实际比例约 1.4995,比理论值略窄一丝。这是像素必须为整数的结果,不是精度缺陷。
- 输出统一是 PNG。裁剪本身是几何操作,用无损 PNG 保存可以保证框内像素与源图逐位相同;PNG 会把原图透明区域一并保留。JPEG 没有透明通道,因此不作为本页的默认输出。
- 源图的 EXIF 元数据不会写进输出。canvas 产出的位图只有像素,相机型号、拍摄时间、GPS 都不会带过去;对在意隐私的人是好事,对想保留拍摄参数的人是损失。
- 一次只处理一张。本页刻意不做批量队列:批量导出容易在命名对应关系上出错,多张素材建议用桌面端图像软件处理。
常见问题
裁剪和「图片尺寸调整」有什么区别?
裁剪改变的是保留原图的哪一块,输出尺寸等于选区的宽 × 高,像素是 1:1 搬运,不重采样;尺寸调整改变的是整张图的像素总量,会按目标宽高重新采样。前者是取景,后者是缩放。想要一张 800×800 的图,如果你要的是「原图里那块正方形」,用本页;如果你要的是「整张图压成 800×800」,那属于尺寸调整。
为什么导出的图和屏幕上看到的方位不一样?
多半是 EXIF 方向在作怪。手机竖拍时像素数据常常是横的,方向记在 Orientation 字段里。本页优先用 createImageBitmap(blob, { imageOrientation: 'from-image' }) 解码,让像素先摆正再取选区坐标;如果浏览器太旧走了 Image 回退路径,极少数机型可能出现方位错位,换用新版 Chrome 或 Firefox 即可。
填的坐标超过原图范围会怎样?
本页会先算 X + 宽 与 Y + 高,超出时直接报错并写明超了多少像素,不让你导出。因为 drawImage 遇到越界不会抛异常,而会把超出的部分当透明,得到的图右缘或下缘凭空多一条空白边,事后很难判断是哪里错了。
为什么不自动把选区夹回图片范围内?
因为自动夹取会悄悄改动你填的数字,你以为是 1600×900,实际导出的是 1400×900,尺寸对不上后续排版。宁可报错并告诉你超了多少,也不替你猜。想快速修正,把「选区 X」按提示减掉超出的像素数即可。
裁剪会不会让画质变差?
不会。本页 dw=sw、dh=sh,缩放比为 1.00,源图里的每个像素原样搬到输出,没有插值也没有重压缩。画质唯一的变化来自导出格式:PNG 是无损的,所以框内像素与源图逐位相同。
为什么选了 3:2 之后高度不是整倍数?
像素只能是整数。宽度 1600 按 3:2 算出的高度是 1066.67,必须取整成 1067,实际比例变成 1600:1067 ≈ 1.4995。要完全精确,请让宽度能被 3 整除(如 1500×1000),或者改用「自由」比例自己指定两个整数。
图片会被上传到服务器吗?
不会。文件通过 File 对象在本地解码,选区取像素用 canvas 完成,导出走 toBlob。想自己确认,打开开发者工具的网络面板,选图并导出,全程不会有新的请求发出。
能一次裁剪很多张吗?
不能。本页刻意做成一次一张:多张图的选区坐标往往各不相同,批量跑容易把命名和坐标对应错。如果你有一批坐标完全相同的图要处理,把四个数抄下来重复操作,比配置批量工具更不容易出错。