網站搜尋

javascript - 我可以關閉 HTML <canvas> 元素上的抗鋸齒嗎?


我正在玩弄 <canvas> 元素,畫線之類的東西。

我注意到我的斜線是抗鋸齒的。我希望在我正在做的事情中能有鋸齒的效果——有辦法關掉這個功能嗎?

答案#1

對於圖像,現在有 context.imageSmoothingEnabled= false。

然而,並沒有任何東西能明確控制線條繪製。你可能需要使用 getImageData 和 putImageData 自行繪製線條(比較麻煩的方式)。

答案#2

在坐標上繪製你的 1 像素 線條,例如 ctx.lineTo(10.5, 10.5)。在點 (10, 10) 上繪製一條一像素線意味著,該位置的這個 1 像素範圍從 9.5 延伸到 10.5,這會導致畫布上繪製出兩條線。

如果你有很多單像素的線條,一個不需要總是將 0.5 加到你想要繪製的實際座標上的好方法,是在一開始對整個畫布使用 ctx.translate(0.5, 0.5)。

答案#3

添加這個:

image-rendering: pixelated; image-rendering: crisp-edges;

將其添加到畫布元素的 style 屬性有助於在畫布上繪製清晰的像素。透過這篇很棒的文章發現的:

https://developer.mozilla.org/zh-TW/docs/Games/Techniques/Crisp_pixel_art_look

答案#4

這可以在 Mozilla Firefox 中完成。將此添加到你的程式碼中:

contextXYZ.mozImageSmoothingEnabled = false;

在 Opera 中,這目前是一個功能請求,但希望它能很快被加入。

答案#5

試試像 canvas { image-rendering: pixelated; } 這樣的方式。

如果你只是想讓一條線不進行抗鋸齒處理,這可能行不通。

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

ctx.fillRect(4, 4, 2, 2);

ctx.strokeStyle = "#11f";
/* The anti-aliasing is worse
   on integer coordinates */
ctx.moveTo(0, 6.5);
ctx.lineTo(10, 7.5);
ctx.lineWidth = 1.0;
ctx.stroke();
canvas {
  image-rendering: pixelated;
  width: 100px;
  height: 100px; /* Scale 10x */
}
<html>
  <head></head>
  <body>
    <canvas width="10" height="10">Canvas unsupported</canvas>
  </body>
</html>

不過我還沒有在很多瀏覽器上測試過這個。

答案#6

我發現了一種更好的方法,可以使用上下文的 filter 屬性來禁用路徑/形狀渲染的抗鋸齒效果:

魔法/簡要版:

ctx = canvas.getContext('2d');

// make canvas context render without antialiasing
ctx.filter = "url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxmaWx0ZXIgaWQ9ImZpbHRlciIgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVDb21wb25lbnRUcmFuc2Zlcj48ZmVGdW5jUiB0eXBlPSJpZGVudGl0eSIvPjxmZUZ1bmNHIHR5cGU9ImlkZW50aXR5Ii8+PGZlRnVuY0IgdHlwZT0iaWRlbnRpdHkiLz48ZmVGdW5jQSB0eXBlPSJkaXNjcmV0ZSIgdGFibGVWYWx1ZXM9IjAgMSIvPjwvZmVDb21wb25lbnRUcmFuc2Zlcj48L2ZpbHRlcj48L3N2Zz4=#filter)";

解密:

資料 URL 是對包含單一濾鏡的 SVG 的引用:

<svg xmlns="http://www.w3.org/2000/svg">
    <filter id="filter" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
        <feComponentTransfer>
            <feFuncR type="identity"/>
            <feFuncG type="identity"/>
            <feFuncB type="identity"/>
            <feFuncA type="discrete" tableValues="0 1"/>
        </feComponentTransfer>
    </filter>
</svg>

然後在 URL 的最末端有一個指向該 #filter 的 id 參考:

"url(data:image/svg+...Zz4=#filter)";

SVG 濾鏡對 alpha 通道使用離散轉換,在渲染時僅選擇完全透明或完全不透明,邊界為 50%。如果需要,可以調整此設定以重新加入一些抗鋸齒,例如:

...
<feFuncA type="discrete" tableValues="0 0 0.25 0.75 1"/>
...

缺點/備註/注意事項

注意,我沒有用圖片測試這個方法,但我可以推測它會影響圖片的半透明部分。我也可以猜測,它可能無法阻止在不同顏色邊界上的圖片抗鋸齒。它不是“最近顏色”解決方案,而是一種二進制透明解決方案。它似乎在路徑/形狀渲染中效果最佳,因為在路徑上只有 alpha 通道會被抗鋸齒處理。

此外,使用最小 lineWidth 為 1 是安全的。更細的線會變得稀疏,或可能經常完全消失。


編輯:

我發現,在 Firefox 中,將 filter 設為 dataurl 並不會立即/同步生效:dataurl 必須先「加載」完成。

例如,以下在 Firefox 中將無法運作:

ctx.filter = "url(data:image/svg+xml;base64,...#filter)";

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(20,20);
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();

ctx.filter = "none";

但等到下一個 JS 幀就可以正常運作:

ctx.filter = "url(data:image/svg+xml;base64,...#filter)";
setTimeout(() => {
    ctx.beginPath();
    ctx.moveTo(10,10);
    ctx.lineTo(20,20);
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 2;
    ctx.stroke();

    ctx.filter = "none";
}, 0);

答案#7

一些背景

當提供非整數座標時,畫布有兩個選擇:

  • 抗鋸齒 - 根據整數座標與非整數座標之間的距離(即四捨五入誤差)來繪製座標周圍的像素。
  • 四捨五入 - 對非整數座標應用某些四捨五入函數(例如 1.4 將變成 1)。

後一種策略對於靜態圖形有效,雖然對於小型圖形(半徑為2的圓),曲線會顯示明顯的階梯,而不是平滑的曲線。

真正的問題是在圖形被平移(移動)時——從一個像素跳到另一個像素(1.6 => 2,1.4 => 1),意味著形狀的原點可能會相對於父容器跳動(不停地向上/下和向左/右移動 1 像素)。

一些小技巧

提示 #1:你可以透過縮放畫布(例如 x 倍)來軟化(或加硬)抗鋸齒,然後自己對幾何圖形應用倒數縮放(1/x)(不要使用畫布)。

比較(無縮放):

使用(畫布縮放:0.75;手動縮放:1.33):

以及(畫布縮放:1.33;手動縮放:0.75):

提示 #2:如果你真的想要鋸齒狀的效果,試著對每個形狀畫幾次(不要擦掉)。每畫一次,抗鋸齒的像素就會變得更深。

比較。繪製一次後:

抽了三次之後:

答案#8

我會使用自訂的線條演算法來繪製所有東西,例如 Bresenham 的線條演算法。看看這個 javascript 實作:http://members.chello.at/easyfilter/canvas.html

我認為這肯定會解決你的問題。