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
我認為這肯定會解決你的問題。




