canvas最大能开多大:先看面积,再看导出格式

0 阅读5分钟

网上搜 canvas 最大尺寸,答案五花八门。有人说 32767 也有人说 16384,还有人只给一个像素总数。我做素材小工具时一直拿其中一个数当上限,直到一张 8192 宽的竖长图顺利导了出来,按那个数它早该失败。那天晚上我自己测了一遍。用的是三个内核构建:Chromium 149 开源构建(跟平常装的 Chrome 不是一回事)、WebKit 26.5 构建(不是 Safari)和 Firefox 151 构建(不是正式版)。测试机是一台 M4 芯片、16 GB 内存的 Mac。每张画布整幅填红,再在左上和右下各点一个 4×4 的色块。读回三个点的颜色都对才算「能画」。导出 PNG 后把文件解开核对宽高和颜色,对了才算「能导出」。

大图解码这一步本身卡不卡,我也顺手看过。我用的是图映 ImgIng(imging.cn/)的压缩转换,其中 10000×10000 的合成 JPEG 转 WebP 和 JPG 都跑完了,整轮换着几张大样本共 12 个用例。界面给了一个可选的「缩到 2048px」建议而我没点,它就按原尺寸处理。网络记录里非 GET 请求是 0 条。

canvas 最大尺寸是多少:单边和面积是两条线

先说单边。另一边固定 8 px 时 Chromium 149 和 Firefox 151 构建都能画到 65,535。WebKit 26.5 构建离谱得多:能画到 4,194,303。可它导出 PNG 只到 1,000,000 而再宽 1 px 回调就是 null。单边这条线平时根本碰不到。真正会撞上的是面积。

Chromium 149 和 WebKit 26.5 构建的面积线都是 268,435,456 像素,也就是 16384×16384。宽 8192 时高能到 32,768 而宽 16384 时高只能到 16,384。我那张竖长图没事就是这个原因:它的宽乘高还在线内。反过来 16384×16385 只多了 1 行就画不上了。Firefox 151 构建是另一套规矩:它的方形能画到 23168×23168,面积差不多是另外两家的 2 倍。宽 8192 时它的高能到 65,535。我拿 4 种宽度去二分最大高度,结果全都符合同一个规律:每行字节按 16 字节对齐后乘以高度要小于 2 GiB。这是我从几个点推出来的,没读过源码。

三个内核能建、能画、能导出的上限对照。重点看「方形上限」那一行:Chromium 149 和 WebKit 26.5 构建都是 16,384²,Firefox 151 构建是 23,168²;第一行说明 getContext 在 10 亿像素时仍返回对象,靠它判断不了任何事

OffscreenCanvas 能画更大吗

我本来指望把活挪进 Worker 能换来更大的画布。实测没有这回事。主线程的 OffscreenCanvas 和 Worker 里的 OffscreenCanvas 阈值都跟 <canvas> 完全相同。三个内核无一例外。

能画不等于能导出:导出格式各有自己的边长线

PNG 这条路上能画就能导出,只有 WebKit 的单边是例外。换成别的格式就不一样了。Chromium 149 导出 WebP 时边长只要超过 16383 就被悄悄裁掉。我画了一张 20000×64 的横向渐变并在右端加一条 64 px 的蓝条。导出来宽度变成 16383 而蓝条整个没了。末列颜色正好等于原图第 16383 列的渐变值。它是直接切掉右边 3617 px,不是缩放。65535×64 更狠,只剩左边 16383 px 等于丢了 75%。JPEG 的线在 65500,超出的部分同样被裁。Firefox 151 构建碰到这两种情况会让 toBlob 回调 null。至少它不会给你一张缺了一截的图。

同一张 20000×1200 画布请求导出 WebP,Chromium 给的短一截,WebKit 给的是原尺寸 PNG,Firefox 没有文件

这张是把源图和三个内核的导出文件并排摆在一起,画布换成了更好看清的 20000×1200。第一条是画布本身存成的 PNG,右端有蓝条。第二条是 Chromium 149 导出的 WebP:宽只剩 16383 而红框里那截是空的。蓝条没了。第三条是 WebKit 26.5 构建,要 WebP 给的却是宽 20000 的 PNG。最后一格属于 Firefox 151 构建。红框里什么都没有是因为回调给了 null。

Chromium 149 里宽 65535 的画布导出 JPEG,右端 1:1 对照,导出文件的蓝条短了一截

JPEG 那条线是在我写的复现小页面上看出来的。宽填 65535 而高填 96 以后先画测试图案再点 Export JPEG。上面一条是画布最右端按 1:1 显示的样子。蓝条完整。下面是导出文件的同一位置:蓝条提前结束,红框里那截没有像素。底下写着 export.jpg 只有 65,500 × 96。右边 35 px 被切掉了。没有报错。

我现在怎么给画布定尺寸

我的工具现在按面积卡而不按边长卡。宽乘高超过 268,435,456 就先缩。导出 WebP 前再单独检查一次边长有没有超过 16383。Firefox 那 2 倍的余量我没去用。三个内核统一按最小的算。WebKit 那个 4,194,303 从哪来我查了一圈也没找到说法。先记在备忘里。