我在開發中使用canvas
的機會不是很多,但是第一次實際使用中就遇到了問題,“很久很久以前,我自己畫了一個雷達圖,線寬都是1像素,但是顯示效果不如期望,這才發現canvas
中的畫線還是有坑的”,對比一下兩個圖,可以發現下圖比較清晰。


我們先畫一個線寬為1像素的線,代碼和顯示效果如下:
const ctx = document.getElementById(canvas).getContext("2d");
ctx.strokeStyle = "red";
ctx.lineWidth =1;
ctx.moveTo(50, 10);
ctx.lineTo(200, 10);
ctx.stroke();

不對呀,這條線咋這么模糊,而且寬度貌似也不是1px
為了畫這條線,瀏覽器首先到達初始起點(50,10)。這條線寬1px,所以兩邊各留0.5px。所以基本上初始起點是從(50,9.5)延伸到(50,10.5)。現在瀏覽器不能在屏幕上顯示0.5像素——最小閾值是1像素。瀏覽器別無選擇,只能將起點的邊界延伸到屏幕上的實際像素邊界。它會在兩邊再加0.5倍的“垃圾”。所以現在,最初的起點是從(50,9)擴展到(50,11),所以看起來有2px寬。情況如下:

下面是真實的2px寬的線,作為對照,可以發現,上面的線寬確實占據了2px,只是邊緣顯示非常模糊

那如何繪制1像素的線呢?本文總結了三種方法。
- 將划線起始點移動到x.5的位置
既然1px寬的線是向兩邊各延伸0.5px,那如果我們的起點直接放在x.5的位置,向左右擴展則剛好占據1px,如下圖所示:
修改代碼
ctx.moveTo(50, 10.5);
ctx.lineTo(200, 10.5);

上面的方法有個壞處是我們要修改現有的計算邏輯,時刻留意要多出0.5px
- 繪制上下文整體移動x.5
這個方法的好處是不擾亂我們的設計尺寸和計算邏輯,操作也很簡單,只需要正常畫好線后,上下文整體移動。
ctx.translate(0, 0.5);
- 控制
canvas
元素大小與繪制上下文大小的比率
canvas
有兩個尺寸,一個是元素本身的css尺寸,一個是繪上下文的尺寸(畫布的尺寸),默認情況下,canvas
的畫布尺寸和元素尺寸都是300*150.比率為1.畫布尺寸是通過直接在元素上添加width
和height
屬性設置的,css尺寸則是通過css樣式屬性設置的,需要注意的是:
1.如果僅在元素上添加width
和height
屬性來設置畫布尺寸,實際表現則同時設置了畫布尺寸和css尺寸為相同的大小;
2.如果僅設置了css尺寸,則畫布尺寸還是默認的300*150,並不會改變;
3.如果同時設置了畫布尺寸和css尺寸,瀏覽器會縮放畫布尺寸來適應css尺寸;
上面的第三條規則給我們畫1px線寬提供了另一種方法,具體方法是設置canvas
的畫布尺寸是css尺寸的兩倍,這樣我們只需要按照兩倍的設計尺寸(2px)來畫線,這樣就不會存在占用半個像素導致模糊的問題,瀏覽器會自動壓縮畫布到它的一半大小,原來2px的線寬會壓縮顯示成1px。
<canvas id="canvas5" width="600" height="300"> </canvas>
<canvas id="canvas6" width="600" height="300"> </canvas>
#canvas5 {
width: 300px;
height: 150px;
}
#canvas6 {
transform-origin: top left;
transform: scale(0.5);
}
如果想畫出真實的物理1px的線,可以將畫布尺寸繼續放大widow.devicePixelRatio
倍