先把八档预设的像素账摊开
上个月给一个应用做商店介绍图,做完上传被打回,理由是尺寸不符。页面文案写的是 1080×1920,可我什么都没碰、点「导出图片」拿到的是 1242×2688、110440 B。这一页的账得自己算。
我把 APP介绍图生成 这一页的八档预设逐个点了一遍。八组数值硬编码在模板第 258–267 行,体积列是质感关掉、未上传截图时的导出字节:
| 预设 | 写死宽×高 | 相对 6.5 寸差 | 导出体积 |
|---|---|---|---|
| iphone65(默认) | 1242×2688 | 0 | 110440 B |
| iphone55 | 1242×2208 | 少 596160 | 94202 B |
| harmonyos | 1080×2340 | 少 811296 | 92081 B |
| oppo·vivo·xiaomi | 1080×2400 | 少 746496 | 93456 B |
| googleplay·custom | 1080×1920 | 少 1264896 | 78985 B |
首屏默认档就是 iphone65(第 257 行),文案那个 1080×1920 只属于 googleplay 与 custom。OPPO、vivo、Xiaomi 同为 1080×2400,三份导出 93456 B 逐字节相同——按钮文字是三档唯一的区别。
图 1:八档其实是 5 种面积。
标签写「2000 × 1000」,成品仍是 1242×2688
宽高框 canvas-w 与 canvas-h 看着像自定义入口,读取路径却在第 434–435 行那个 currentPreset === custom 分支里。6.5 寸档把两框改成 2000 与 1000,标签立刻变「2000 × 1000」,画布仍是 1242×2688,成品 IHDR 声明 [1242,2688]、112233 B;切到自定义再喂同样两个数,才导出 [2000,1000] 72954 B。8 档里 7 档这样改只动标签、零提示。
上传的截图不会被重新裁:源矩形恒定
换画布尺寸不会把已上传的截图重裁一遍。同一张 1080×1920 的图,画布分别换成 540×960、2160×3840、400×2000、1080×1200,drawImage 的源矩形四个参数恒为 71.70731707317071, 0, 936.5853658536586, 1920,只有目标矩形跟画布走。机身高恒等于宽的 2.05 倍(第 513 行),裁切比锁在 1/2.05 = 0.48780487804878053。
一张 6000×4000 的图有 2400 万像素,解码后只有 1119518 个进画布,即 4.66%,传更大的图不增加成品像素量。
手机框:宽占比恒定,高占比随机型漂
机身内宽占比只由 shot-phone-scale 决定,恒等于 scale×0.85、与机型无关,70 这档就是 59.5000%。高度占比却不守恒:6.5 寸档内高 56.3590%、googleplay 档 68.6109%,同一个 scale 差 12.2519 个百分点,机身高只由宽度连乘出来。
默认参数下的越界更要紧:1080×1920 档机身底边 2054.93,被切掉 134.93 px;1242×2208 档溢出 153.97 px;反倒 1242×2688 留了 143.63 px 余量。8 档里至少 2 档默认就把手机框顶出画布。
图 2:默认参数下的机身底边与画布高。
预览与导出:同一条绘制路径,diffPx=0
exportImage(第 699–723 行)把 #shot-canvas 整块拷进同尺寸的临时画布再编成 PNG。6.5 寸默认态下把 3338496 个像素逐点比对,diffPx=0,预览与成品一个通道都不差。两条绿色拖拽线和「文案区」「手机区」底板也被原样烙进成品,第 700/707/712 行注释说的先清除什么都没做。exportAll 的 5 张同理。这条账只覆盖画布到 PNG 一段:导出不做提交也不触发重绘,画布里有没有最新文字取决于此前渲染过没有。
纹理是这张页最大的一笔隐藏体积
同一张 1242×2688、无截图,只切质感下拉框:none 110440 B → grid 115234 B(+4794)→ dots 131365 B(+20925)→ noise 2742315 B(24.83 倍)。噪点要把画布读回内存逐像素改色,改动量等于画布像素数。
它的随机源是 Math.random()(第 673 行)。600×1000 带照片、基线 234553 B 那组三档分别加 1818、4515、458584 B,而噪点连点两次得 693137 B 与 689989 B;固定成 0.5 后与 none 逐字节相同。这一档体积不可复现,另三档才谈得上幂等——关纹理连点 3 次恒 659546 B。
画布面积的硬账:268435456 px 之后全程静默
逐档二分实测到的临界面积是 268435456 px,也就是 16384×16384;同一批里 16385 见方就画不出东西。失效形态是最贵的一条边界:取上下文仍返回对象、canvas.width 仍是请求值、渲染不抛错、导出拿到空结果——没有任何一处代码检查过宽高乘积。
缩放与空值
preview-zoom 只改 CSS 不改分辨率(第 438–441 行):zoom 33 起被 min(400/w, zoom) 钉死在 400×866,后备画布一直是 1242×2688,滑块标签却继续显示 100%,而两种缩放下导出的是同一个文件。
两类控件的清空语义也打架:number 框不钳位,-50 让标签写「-50 × -50」而画布回落默认的 300×150;range 滑块反过来会钳位归中,文案位置清空得 14、手机位置清空得 48。
至于上架时怎么和商店那边打交道,我另有一篇 App Store 介绍图的旧笔记,本篇不重复,只算这一页的像素与字节。
三个追问
Q:改了画布宽高输入框,为什么导出还是原来的尺寸?
那两个框只在自定义档被读取(第 434–435 行),其余 7 档改完只动标签。验证办法是下载后读文件头声明的尺寸。
Q:三台安卓机型的导出图为什么完全一样?
三档写死的都是同一个 1080×2400,三份 PNG 逐字节相同;要拉开差异得切自定义档改宽高。
Q:成品里为什么带着绿色手柄线和区域标签?
逐点比对 diffPx=0,画布上有什么就编出什么,而这两条线每帧都画在画布上。想要干净成品只能导出后再裁一次。