雜記 › 技術筆記 › 替應用做商店介紹圖,點匯出卻拿到 1242×2688:8 檔畫布預設的像素帳、噪點 24.83 倍體積與預設被裁掉 134.93 px 的手機框

替應用做商店介紹圖,點匯出卻拿到 1242×2688:8 檔畫布預設的像素帳、噪點 24.83 倍體積與預設被裁掉 134.93 px 的手機框

八檔畫布預設寫死的像素與匯出體積對不上,寬高輸入框只在自訂檔生效,預設檔機身底邊被切掉 134.93 px,噪點一檔讓體積漲到 24.83 倍。

先把八檔預設的像素帳攤開

上個月替一個應用做商店介紹圖,做完上傳被打回,理由是尺寸不符。頁面文案寫的是 1080×1920,可我什麼都沒碰、點「匯出圖片」拿到的是 1242×2688、110440 B。這一頁的帳得自己算。

我把 APP 介紹圖產生器 這一頁的八檔預設逐個點了一遍。八組數值硬編碼在模板第 258–267 行,體積列是質感關掉、未上傳截圖時的匯出位元組:

預設寫死寬×高相對 6.5 吋差匯出體積
iphone65(預設)1242×26880110440 B
iphone551242×2208少 59616094202 B
harmonyos1080×2340少 81129692081 B
oppo·vivo·xiaomi1080×2400少 74649693456 B
googleplay·custom1080×1920少 126489678985 B

首屏預設檔就是 iphone65(第 257 行),文案那個 1080×1920 只屬於 googleplay 與 custom。OPPO、vivo、Xiaomi 同為 1080×2400,三份匯出 93456 B 逐位元組相同——按鈕文字是三檔唯一的區別。

畫布面積(px) 6.5 吋 3338496 5.5 吋 2742336 鴻蒙 2527200 安卓三檔 2592000 兩檔同值 2073600

圖 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 檔預設就把手機框頂出畫布。

畫布高 機身底邊 6.5 吋 2544.37 餘 143.63 5.5 吋 2361.97 溢出 153.97 Google 2054.93 溢出 134.93

圖 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,畫布上有什麼就編出什麼,而這兩條線每幀都畫在畫布上。想要乾淨成品只能匯出後再裁一次。

📎 相關文章

16 組開關實測 HTML 去標籤:報 19 個標籤,實際刪掉 148 個字元 2026-10-30 SVG 轉 PNG 後外部引用的圖丟了一半:同一份 594 B 檔案預覽有 3 張圖、成品只剩 1 張 2026-10-30 二進位轉 Base64 這一頁:前綴只有 5 檔,換行能解、連字號和底線直接報錯 2026-10-30 16 個鍵進、4 個欄出:JSON 轉表格的表頭聯集、點號展平與 CSV 引號規則 2026-10-30 取消勾選「保留註解」後,format-js 把 24 字元 URL 壓成 13 字元殘缺碼 2026-10-30