解決 canvas 繪圖在高清屏中的模糊問題
為什么模糊
CSS 像素是一個抽象單位(1 px),瀏覽器根據某種規則將 css 像素轉化為屏幕需要的實際像素值。
在高清屏之前,屏幕上顯示一個像素點需要 1 x 1 個 css 像素。在高清屏,同樣大小的屏幕上要顯示一個點,就需要 n x 1 個 css 像素。這里的 n 就是設備像素比 devicePixelRatio >= 2.
也就是說,同樣大小的區域,高清屏需要更多的 css 像素:css 像素不夠,則會放大內容——變得模糊;css 像素足夠,則會縮小內容——變得清晰。放大、縮小的比例由 devicePixelRatio 決定。
比如說 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 = canvas.width * scale
canvas.height = canvas.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),亦稱為點陣圖像或繪制圖像,是由稱作像素(圖片元素)的單個點組成的。當放大位圖時,圖像就失真、模糊了。