先把八檔預設的像素帳攤開
上個月替一個應用做商店介紹圖,做完上傳被打回,理由是尺寸不符。頁面文案寫的是 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:三個 Android 機型的匯出圖為什麼完全一樣?
三檔寫死的都是同一個 1080×2400,三份 PNG 逐位元組相同;要拉開差異得切自訂檔改寬高。
Q:成品裡為什麼帶著綠色手柄線和區域標籤?
逐點比對 diffPx=0,畫布上有什麼就編出什麼,而這兩條線每幀都畫在畫布上。想要乾淨成品只能匯出後再裁一次。