解決 canvas 繪圖在高清屏中的模糊問題
解決 canvas 繪圖在高清屏中的模糊問題
為什麼模糊
我們知道,CSS 畫素是一個抽象單位(1 px),瀏覽器根據某種規則將 css 畫素轉化為螢幕需要的實際畫素值。
在高清屏之前,螢幕上顯示一個畫素點需要 1 x 1 個 css 畫素。高清屏出現後,同樣大小的螢幕上要顯示一個點,就需要 n x 1 個 css 畫素。這裡的 n 就是裝置畫素比 devicePixelRatio >= 2.
也就是說,同樣大小的區域,高清屏需要更多的 css 畫素:css 畫素不夠,則會放大內容——變得模糊;css 畫素足夠,則會縮小內容——變得清晰。
比如說 iPhone 4s,它的 devicePixelRatio 為 2,假設螢幕上有塊區域大小為 100px x 100px,上面有張 100px x 100px大小的圖片,那麼這張圖片會被放大 2 倍後渲染到這塊區域,所以看起來就模糊了。
canvas 繪圖在高清屏中模糊
canvas 屬於點陣圖,繪製在它上面的文字、圖片、線條也屬於點陣圖,經放大後就失真、顯得模糊了。要解決這個問題,我們就需要 canvas 擁有更多的 css 畫素,即讓 canvas 足夠大。多大才夠呢?太大會不會浪費資源/效能?我們需要因地制宜,根據 devicePixelRatio 來決定畫布的大小。
function setupCanvas(canvas) { var dpr = window.devicePixelRatio || 1 var rect = canvas.getBoundingClientRect() canvas.width = rect.width * dpr canvas.height = rect.height * dpr var ctx = canvas.getContext('2d') ctx.scale(dpr, dpr) return ctx } // 現在我們只需要根據 UI 設計圖繪製需要的內容 // 由於使用了 setupCanvas,繪製的內容在各種高清屏中表現清晰、一致 var ctx = setupCanvas(document.querySelector('.my-canvas')); ctx.lineWidth = 5; ctx.beginPath(); ctx.moveTo(100, 100); ctx.lineTo(200, 200); ctx.stroke();
在 setupCanvas 函式中,我們根據螢幕的 devicePixelRatio 對 canvas 畫布本身進行放大,然後使用 ctx.scale() 放大 canvas 單位,這樣在 ctx 上下文繪製的內容就會被放大,使得最後生成的圖片清晰的顯示在對應的螢幕上。
在實際工作中,我們還要兼顧不同大小不同解析度的螢幕。一般設計師會給我們寬度為 375px 的設計圖,這需要我們根據螢幕大小進行縮放:
function setupCanvas(canvas) { const UI_WIDTH = 375 const DOC_WIDTH = document.documentElement.clientWidth const DPR = window.devicePixelRatio || 1 let scale = (DPR * DOC_WIDTH / UI_WIDTH).toFIxed(2) let rect = canvas.getBoundingClientRect() canvas.width = rect.width * scale canvas.height = rect.height * scale let ctx = canvas.getContext('2d') ctx.scale(scale, scale) return ctx }
術語
device pixel
裝置畫素(又稱物理畫素、螢幕畫素)是顯示屏的最小物理單元,是我們在螢幕上能看到的最小點。
device-independent pixel
裝置無關畫素(又稱密度無關畫素,DIP)是一個抽象單位,表示計算機中的一個虛擬點,由系統轉為一個裝置畫素點。
devicePixelRatio
裝置畫素比,它的值等於裝置畫素/裝置無關畫素,devicePixelRatio = DP/DIP
css pixel
css 畫素是瀏覽器使用的抽象單位,屬於裝置無關畫素(DIP)。我們看到的內容,是瀏覽器將 css 畫素轉化為裝置畫素後的結果。
向量圖
向量圖是根據幾何特性來繪製圖形,向量可以是一個點或一條線,向量圖只能靠軟體生成,檔案佔用內在空間較小,因為這種型別的影象檔案包含獨立的分離影象,可以自由無限制的重新組合。它的特點是放大後圖像不會失真。
點陣圖
點陣圖影象(bitmap),亦稱為點陣影象或繪製圖像,是由稱作畫素(圖片元素)的單個點組成的。當放大點陣圖時,影象就失真、模糊了。