雜記 › 技術筆記 › 鎖是單向的、百分比是兩把尺:pct 50 得 619×430,PNG 四檔 quality 全 6971 B

鎖是單向的、百分比是兩把尺:pct 50 得 619×430,PNG 四檔 quality 全 6971 B

寬高鎖只讀寬、只寫高,手打的高會被打回;百分比是寬高各跑一次 Math.round,pct 50 得 619×430;同一張 PNG 四檔 quality 全 6971 B,逐筆實測。

兩個框為什麼會在一張圖上互相打架

一張自造的 1237×859 純色圖(非照片)拖進去,鎖著時把寬打 800,高框寫回「556」;再把高框手打成 300,它又變回「556」。滑桿那頭:同一張 PNG 縮到 619×430,品質從 10 拖到 100,四輪結果行都是「619 × 430 | 6.8 KB」。這頁模板 12308 位元組 / 265 行,腳本原樣跑兩路:node:vm 樁與 Chrome 154 真編碼器。

被測的是圖片尺寸調整,全頁本地處理,grep fetch( 命中 0 處。

鎖只讀寬、只寫高

onSizeInput(L188–L192)鎖著時只讀寬框、只算 Math.round(寬 / aspectRatio)、只寫高框。aspectRatio 存的是 1237/859=1.440046565774156 這個浮點商,不是既約比,於是手打的高必被覆蓋:寬 800 + 手打高 300 → 高框留「556」。toggleLock(L181–L185)解鎖後兩框各走各的。

鎖著時寬框高框除法原值
859597596.5084882780922

這把尺在原圖上共掃 18 檔(1237→「859」、400→「278」),寬打 1 時靠進位保住高 1。

百分比是兩把獨立的尺

onPercentInput(L195–L200)對寬高各跑一次 Math.round,完全不看 aspectLocked。pct=50 → 619×430,理想 618.5 × 429.5,兩框之和 1049 對 1048、差 1。兩軸同時以 .5 結尾的 p 有 10 檔,Math.round 對正 .5 一律進位;pct 從 1 掃到 1000,120 檔兩條路的高不等。

同一張 1237×859,兩條路的高 pct=1 → 12 × 9 鎖尺 → 12 × 8 分歧:1.3333… 對 1.5 寬 309 / 619 / 928 / 1856 四檔重合,屬巧合檔。

圖 1:pct=1 那檔兩條路給出兩種長寬比。

比例框從不回頭讀寬高框

switchMode(L172–L178)只改 currentMode 與兩個面板的 display,沒有任何一行程式碼回寫 resize-percent。點 150% → 切回 pixel → 把寬手打 400(高被算成 278),比例框全程仍顯示「150」;反推值 100*1856/1237=150.0404% 不等於框裡的 150——框裡存的是「點過的檔」。再點一次 setPercent(150)(L203–L206)就把兩框打回 1856×1289,剛手打的 400 蒸發。

滑桿在 PNG 上為什麼一個位元組都不動

本機 Chrome 154 只有三個真編碼器,1×1 畫布上 png / jpeg / webp 分別回 110 / 762 / 552 B。L229 把滑桿值除 100 交給 toBlob,PNG 編碼器不吃這個參數;BMP 又疊一層——L228 把輸出改寫成 PNG,而 L152 只給 image/jpeg / image/webp 顯示品質行,滑桿本身 display:none。

619×430 的 blobq10q50q90q100
PNG6971697169716971
JPEG2338234023413921
WEBP1092110210504684

JPEG 的 10→90 三檔只差 3 B;WEBP 的 q90 比 q10 小(1050<1092),編碼器不保證單調;BMP 源(3188662 B)六檔全 6971 B。同族幾頁共用這一份模板,那幾頁的位元組帳不在本篇。順帶說明:這組數字全部出自同一份 blob 的四次重新編碼,而不是四次重新縮放。

結果行那串字面怎麼拼

L235 是 w + ' × ' + h + ' | ' + formatSize(blob.size),w/h 取 JS 裡的整數而非畫布屬性,「 × 」是 3 碼元/4 位元組。formatSize(L165–L169)分三檔:B 檔原樣整數、KB 檔 toFixed(1)、MB 檔 toFixed(2),閾值 1024 與 1048576,實測 1024→「1.0 KB」、1075→「1.0 KB」而 1076→「1.1 KB」、1048576→「1.00 MB」。壓位元組走另一條通道,見圖片壓縮;那篇透傳的實測量的是別一頁。

三閘與它攔不住的那些

doResize(L209–L237)只三道閘:無圖、parseInt 出 NaN 或 ≤0、w>10000 || h>10000;JS 不設下限校驗,min="1" / max="10000" 只是控件屬性:「10001」瀏覽器不攔,走到 L216 才彈「尺寸不能超過 10000px」。最安靜的是「1e5」:parseInt 遇 e 停讀成 1,無 toast 真出圖,結果行「1 × 1 | 762 B」,輸入與產出差 5 個數量級。只有滑桿被瀏覽器硬夾(打 9 →「10」),L229 因此落在 10..100。

失敗之後屏上留著的是上一張

出過「1 × 1 | 762 B」之後再打 w=619 / h=0,彈「請輸入有效的寬高尺寸」,結果行仍讀上一句。更實在的是不落地那格:先出 619×430 的 webp(「619 × 430 | 1.0 KB」、1050 B),再打 10000×10000 的 webp:回調不來,畫布屬性卻已被 L221–L222 同步改成 10000x10000,結果行仍是上一句、下載仍是上一檔那 1050 B。resetAll(L253–L263)之後七個狀態量逐字不變。

批量是實測出來的「沒有」:files.length 0 處、for( 0 處、progress 0 處,兩處 files[ 只取 [0];一次拖入 2 個檔案只有第 1 張進 handleFile。重採樣演算法與 imageSmoothingQuality(頁面從不讀它,無像素級判據可反推核函數)、佇列與耗時(無佇列程式碼即無物件可測)、介面渲染像素(無渲染器判據),一律標未實測。

順帶對一眼文案

config/tools.php 的 image-resize 條目 16 條主張逐條判:[一致] 6、[部分一致] 7、[衝突] 3。衝突三條:「另一邊」只對一半、「證件照」檔鎖著時輸入 295 得 205 而不是 413(差 -208,要先解鎖而文案未提)、「縮圖批量」零通道。

常見問題

Q:鎖著時手打高度為什麼被打回?

鎖只在寬→高一個方向寫,手打值在回寫那一步被覆蓋。

Q:pct 50 得 619×430,是等比嗎?

它是寬高各過一次 Math.round:619/430=1.4395348837209303 對源比差 -0.0355%,1..1000 裡 120 檔與鎖尺不等。

Q:滑桿拉滿為什麼位元組沒變?

PNG 與被 L228 改寫成 PNG 的 BMP 不吃 quality,四輪同字面;只有 JPEG / WEBP 變位元組。

📎 相關文章

星號段長寫死:脫敏頁五套掩碼固定切出 4、3、11、12 枚星號,18 碼元串被手機檔搶走、空框回落 * 2026-11-13 相對時間的「一個月」寫死 30 天:時間戳頁六檔除數、13 位毫秒輸入與 UTC+8/UTC-5 兩檔截斷實測 2026-11-13 兩個 tab 各算一遍同一個 C(n,r):n=20 寫滿 40 個因子、n=21 一格不剩,300 位大數與 r=0 的兩種讀法 2026-11-13 8 個元素算得出 40320 條卻只列 5000 行:排列計算器的兩道互不相干的上限、重複元素按 3! 計數與 n≤20 的過程斷點 2026-11-12 投放連結的裝配順序與大小寫摺疊:UTM 產生器六態標註、兩道硬上限與五欄各用一把尺實測 2026-11-12