Canvas中如何畫一條清晰的線寬為奇數(如1px邏輯像素)的線?


我在開發中使用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.畫布尺寸是通過直接在元素上添加widthheight屬性設置的,css尺寸則是通過css樣式屬性設置的,需要注意的是:

1.如果僅在元素上添加widthheight屬性來設置畫布尺寸,實際表現則同時設置了畫布尺寸和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

參考
How to Draw 1px Crisp Lines in HTML5 Canvas


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM