Html HTML5 Canvas 和抗锯齿
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4261090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HTML5 Canvas and Anti-aliasing
提问by KRouane
How to turn on the anti-aliasingon an canvas.
如何打开画布上的抗锯齿功能。
The following code doesn't draw a smooth line:
以下代码未绘制平滑线:
var context = mainCanv.getContext("2d");
if (context) {
context.moveTo(0,0);
context.lineTo(100,75);
context.strokeStyle = "#df4b26";
context.lineWidth = 3;
context.stroke();
}
采纳答案by Gaurav
Anti-aliasing cannot be turned on or off, and is controlled by the browser.
抗锯齿不能打开或关闭,由浏览器控制。
回答by Bacher
You may translate canvas by half-pixel distance.
您可以按半像素距离平移画布。
ctx.translate(0.5, 0.5);
Initially the canvas positioning point between the physical pixels.
最初是物理像素之间的画布定位点。
回答by zachdyer
I haven't needed to turn on anti-alias because it's on by default but I have needed to turn it off. And if it can be turned off it can also be turned on.
我不需要打开抗锯齿,因为默认情况下它是打开的,但我需要关闭它。如果它可以关闭,它也可以打开。
ctx.imageSmoothingEnabled = true;
I usually shut it off when I'm working on my canvas rpg so when I zoom in the images don't look blurry.
当我在画布 rpg 上工作时,我通常会关闭它,所以当我放大图像时不会看起来很模糊。
回答by Kaiido
It's now 2018, and we finally have cheap ways to do something around it...
现在是 2018 年,我们终于有了廉价的方法来解决它……
Indeed, since the 2d context API now has a filter
property, and that this filter property can accept SVGFilters, we can build an SVGFilter that will keep only fully opaque pixels from our drawings, and thus eliminate the default anti-aliasing.
事实上,由于 2d 上下文 API 现在有一个filter
属性,并且这个过滤器属性可以接受SVGFilters,我们可以构建一个 SVGFilter 来只保留我们绘图中完全不透明的像素,从而消除默认的抗锯齿。
So it won't deactivate antialiasing per se, but provides a cheap way both in term of implementation and of performances to remove all semi-transparent pixels while drawing.
因此,它本身不会停用抗锯齿,但在实现和性能方面都提供了一种廉价的方式来在绘图时删除所有半透明像素。
I am not really a specialist of SVGFilters, so there might be a better way of doing it, but for the example, I'll use a <feComponentTransfer>
node to grab only fully opaque pixels.
我并不是真正的 SVGFilters 专家,所以可能有更好的方法来做这件事,但对于这个例子,我将使用一个<feComponentTransfer>
节点来只抓取完全不透明的像素。
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ABEDBE';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'black';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
// first without filter
ctx.fillText('no filter', 60, 20);
drawArc();
drawTriangle();
// then with filter
ctx.setTransform(1, 0, 0, 1, 120, 0);
ctx.filter = 'url(#remove-alpha)';
// and do the same ops
ctx.fillText('no alpha', 60, 20);
drawArc();
drawTriangle();
// to remove the filter
ctx.filter = 'none';
function drawArc() {
ctx.beginPath();
ctx.arc(60, 80, 50, 0, Math.PI * 2);
ctx.stroke();
}
function drawTriangle() {
ctx.beginPath();
ctx.moveTo(60, 150);
ctx.lineTo(110, 230);
ctx.lineTo(10, 230);
ctx.closePath();
ctx.stroke();
}
// unrelated
// simply to show a zoomed-in version
var zCtx = zoomed.getContext('2d');
zCtx.imageSmoothingEnabled = false;
canvas.onmousemove = function drawToZoommed(e) {
var x = e.pageX - this.offsetLeft,
y = e.pageY - this.offsetTop,
w = this.width,
h = this.height;
zCtx.clearRect(0,0,w,h);
zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3);
}
<svg width="0" height="0" style="position:absolute;z-index:-1;">
<defs>
<filter id="remove-alpha" x="0" y="0" width="100%" height="100%">
<feComponentTransfer>
<feFuncA type="discrete" tableValues="0 1"></feFuncA>
</feComponentTransfer>
</filter>
</defs>
</svg>
<canvas id="canvas" width="250" height="250" ></canvas>
<canvas id="zoomed" width="250" height="250" ></canvas>
And for the ones that don't like to append an <svg>
element in their DOM, you can also save it as an external svg file and set the filter
property to path/to/svg_file.svg#remove-alpha
.
对于那些不喜欢<svg>
在 DOM 中附加元素的人,您还可以将其另存为外部 svg 文件并将filter
属性设置为path/to/svg_file.svg#remove-alpha
.
回答by Overcode
Here's a workaround that requires you to draw lines pixel by pixel, but will prevent anti aliasing.
这是一种解决方法,需要您逐个像素地绘制线条,但会防止抗锯齿。
// some helper functions
// finds the distance between points
function DBP(x1,y1,x2,y2) {
return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
// finds the angle of (x,y) on a plane from the origin
function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); }
// the function
function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
var dist = DBP(sx,sy,tx,ty); // length of line
var ang = getAngle(tx-sx,ty-sy); // angle of line
for(var i=0;i<dist;i++) {
// for each point along the line
ctx.fillRect(Math.round(sx + Math.cos(ang)*i), // round for perfect pixels
Math.round(sy + Math.sin(ang)*i), // thus no aliasing
1,1); // fill in one pixel, 1x1
}
}
Basically, you find the length of the line, and step by step traverse that line, rounding each position, and filling in a pixel.
基本上,您找到线的长度,并逐步遍历该线,四舍五入每个位置,并填充一个像素。
Call it with
调用它
var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)
回答by Jordan
If you need pixel level control over canvas you can do using createImageDataand putImageData.
如果您需要对画布进行像素级控制,您可以使用createImageData和putImageData。
HTML:
HTML:
<canvas id="qrCode" width="200", height="200">
QR Code
</canvas>
And JavaScript:
和 JavaScript:
function setPixel(imageData, pixelData) {
var index = (pixelData.x + pixelData.y * imageData.width) * 4;
imageData.data[index+0] = pixelData.r;
imageData.data[index+1] = pixelData.g;
imageData.data[index+2] = pixelData.b;
imageData.data[index+3] = pixelData.a;
}
element = document.getElementById("qrCode");
c = element.getContext("2d");
pixcelSize = 4;
width = element.width;
height = element.height;
imageData = c.createImageData(width, height);
for (i = 0; i < 1000; i++) {
x = Math.random() * width / pixcelSize | 0; // |0 to Int32
y = Math.random() * height / pixcelSize| 0;
for(j=0;j < pixcelSize; j++){
for(k=0;k < pixcelSize; k++){
setPixel( imageData, {
x: x * pixcelSize + j,
y: y * pixcelSize + k,
r: 0 | 0,
g: 0 | 0,
b: 0 * 256 | 0,
a: 255 // 255 opaque
});
}
}
}
c.putImageData(imageData, 0, 0);
回答by TOGD
so I am assuming this is kinda out of use now but one way to do it is actually using document.body.style.zoom=2.0;
but if you do this then all of your canvas measurements will have to be divided by the zoom. Also, set the zoom higher for more aliasing. This is helpful because it is adjustable. Also if using this method, I suggest that you make functions to do the same as ctx.fillRect()
etc. but with the zoom taken into account. E.g.
所以我假设这现在有点用不了了,但一种方法是实际使用,document.body.style.zoom=2.0;
但如果你这样做,那么你所有的画布测量值都必须除以缩放。此外,将缩放设置得更高以获得更多锯齿。这很有用,因为它是可调节的。此外,如果使用这种方法,我建议您使用与ctx.fillRect()
等相同的功能,但要考虑缩放。例如
function fillRect(x, y, width, height) {
var zoom = document.body.style.zoom;
ctx.fillRect(x/zoom, y/zoom, width/zoom, height/zoom);
}
Hope this helps!
希望这可以帮助!
Also, a sidenote: this can be used to sharpen circle edges as well so that they don't look as blurred. Just use a zoom such as 0.5!
另外,旁注:这也可用于锐化圆形边缘,使它们看起来不那么模糊。只需使用像 0.5 这样的变焦!