Javascript 在画布中绘制 1px 粗线会创建一条 2px 粗线

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13879322/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:01:44  来源:igfitidea点击:

Drawing a 1px thick line in canvas creates a 2px thick line

javascripthtmlhtml5-canvasline

提问by MintDeparture

In this jsfiddle there's a line with a lineWidth of 1.

在这个 jsfiddle 中有一条 lineWidth 为 1 的线。

http://jsfiddle.net/mailrox/9bMPD/350/

http://jsfiddle.net/mailrox/9bMPD/350/

e.g:

例如:

ctx.lineWidth = 1;

However the line is 2px thick when it's drawn on the canvas, how do you create a 1px thick line.

然而,当它在画布上绘制时,这条线是 2px 粗的,你如何创建一条 1px 粗的线。

I could draw a rectangle (with 1px height) however I want the line to also work on diagonals. So how do you get this line to be 1px high?

我可以绘制一个矩形(高度为 1px),但是我希望这条线也适用于对角线。那么如何让这条线达到 1px 高呢?

Thanks!

谢谢!

回答by Ferry Kobus

Canvas calculates from the half of a pixel

Canvas 从像素的一半开始计算

ctx.moveTo(50,150.5);
ctx.lineTo(150,150.5);

So starting at a half will fix it

所以从一半开始会解决它

Fixed version: http://jsfiddle.net/9bMPD/357/

固定版本:http: //jsfiddle.net/9bMPD/357/

This answerexplains why it works that way.

这个答案解释了为什么它会这样工作。

回答by Richard

You can also translate by half a pixel in the X and Y directions and then use whole values for your coordinates (you may need to round them in some cases):

您还可以在 X 和 Y 方向上平移半个像素,然后使用整个坐标值(在某些情况下您可能需要将它们四舍五入):

context.translate(0.5, 0.5)

context.moveTo(5,5);
context.lineTo(55,5);

Keep in mind that if you resize your canvas the translate will be reset - so you'll have translate again.

请记住,如果您调整画布大小,翻译将被重置 - 因此您将再次进行翻译。

This answerexplains why it works that way.

这个答案解释了为什么它会这样工作。

回答by tonycoupland

Or as thisanswer states, to get a width of 1, you need to start at a half pixel.

或者正如此答案所述,要获得 1 的宽度,您需要从半像素开始。

ctx.moveTo(50.5,150.5);
ctx.lineTo(150.5,150.5);

http://jsfiddle.net/9bMPD/355/

http://jsfiddle.net/9bMPD/355/

回答by RobIII

Did you see the first hit on google? (search for canvas line width 1px). Though I have to admit this isn't exactly "clean" or "lean". Ferry Kobus'solution is much better. Then again: it sucks you need to use "half pixels" in the first place...

你看到谷歌上第一个热门吗?(搜索canvas line width 1px)。虽然我不得不承认这并不完全是“干净”或“精益”。Ferry Kobus 的解决方案要好得多。再说一次:它很糟糕,您首先需要使用“半像素”...

回答by Tom Ah

The Canvas can draw clean straight lines with fillRect(). A rectangle with a 1px height or a 1px width does the job. It doesn't need half-pixel value:

Canvas 可以使用 fillRect() 绘制干净的直线。一个高度为 1px 或宽度为 1px 的矩形可以完成这项工作。它不需要半像素值:

var ctx = document.getElementById("myCanvas").getContext("2d");

ctx.drawVerticalLine = function(left, top, width, color){
    this.fillStyle=color;
    this.fillRect(left, top, 1, width);
};

ctx.drawHorizontalLine = function(left, top, width, color){
    this.fillStyle=color;
    this.fillRect(left, top, width, 1);
}

ctx.drawVerticalLine(150, 0, 300, "green");
ctx.drawHorizontalLine(0, 150, 300, "red");

https://jsfiddle.net/ynur1rab/

https://jsfiddle.net/ynur1rab/

回答by Ievgen Naida

For me, only a combination of different 'pixel perfect' techniques helped to archive the results:

对我来说,只有不同的“像素完美”技术的组合有助于存档结果:

  1. Get and scale canvas with the pixel ratio:

    pixelRatio = window.devicePixelRatio/ctx.backingStorePixelRatio

  2. Scale the canvas on the resize (avoid canvas default stretch scaling).

  3. multiple the lineWidth with pixelRatio to find proper 'real' pixel line thickness:

    context.lineWidth = thickness * pixelRatio;

  4. Check whether the thickness of the line is odd or even. add half of the pixelRatio to the line position for the odd thickness values.

    x = x + pixelRatio/2;

  1. 使用像素比率获取和缩放画布:

    pixelRatio = window.devicePixelRatio/ctx.backingStorePixelRatio

  2. 在调整大小时缩放画布(避免画布默认拉伸缩放)。

  3. 将 lineWidth 与 pixelRatio 相乘以找到合适的“真实”像素线粗:

    context.lineWidth = 厚度 * pixelRatio;

  4. 检查线的粗细是奇数还是偶数。将像素比率的一半添加到奇数厚度值的线位置。

    x = x + pixelRatio/2;

The odd line will be placed in the middle of the pixel. The line above is used to move it a little bit.

奇数线将放置在像素的中间。上面的线用于稍微移动它。

function getPixelRatio(context) {
  dpr = window.devicePixelRatio || 1,
    bsr = context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio ||
    context.backingStorePixelRatio || 1;

  return dpr / bsr;
}


var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var pixelRatio = getPixelRatio(context);
var initialWidth = canvas.clientWidth * pixelRatio;
var initialHeight = canvas.clientHeight * pixelRatio;


window.addEventListener('resize', function(args) {
  rescale();
  redraw();
}, false);

function rescale() {
  var width = initialWidth * pixelRatio;
  var height = initialHeight * pixelRatio;
  if (width != context.canvas.width)
    context.canvas.width = width;
  if (height != context.canvas.height)
    context.canvas.height = height;

  context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
}

function pixelPerfectLine(x) {

  context.save();
  context.beginPath();
  thickness = 1;
  // Multiple your stroke thickness  by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;

  context.strokeStyle = "Black";
  context.moveTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 0));
  context.lineTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 200));
  context.stroke();
  context.restore();
}

function pixelPerfectRectangle(x, y, w, h, thickness, useDash) {
  context.save();
  // Pixel perfect rectange:
  context.beginPath();

  // Multiple your stroke thickness by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;
  context.strokeStyle = "Red";
  if (useDash) {
    context.setLineDash([4]);
  }
  // use sharp x,y and integer w,h!
  context.strokeRect(
    getSharpPixel(thickness, x),
    getSharpPixel(thickness, y),
    Math.floor(w),
    Math.floor(h));
  context.restore();
}

function redraw() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  pixelPerfectLine(50);
  pixelPerfectLine(120);
  pixelPerfectLine(122);
  pixelPerfectLine(130);
  pixelPerfectLine(132);
  pixelPerfectRectangle();
  pixelPerfectRectangle(10, 11, 200.3, 443.2, 1, false);
  pixelPerfectRectangle(41, 42, 150.3, 443.2, 1, true);
  pixelPerfectRectangle(102, 100, 150.3, 243.2, 2, true);
}

function getSharpPixel(thickness, pos) {

  if (thickness % 2 == 0) {
    return pos;
  }
  return pos + pixelRatio / 2;

}

rescale();
redraw();
canvas {
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-crisp-edges;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  width: 100vh;
  height: 100vh;
}
<canvas id="canvas"></canvas>

Resize event is not fired in the snipped so you can try the file on the github

Resize 事件不会在截图中触发,因此您可以尝试github上的文件

回答by Curtis

If none of these answers worked for you, check your browser zoom. Mine was somehow at 125% so every fourth 1px line was drawn 2px wide.

如果这些答案都不适合您,请检查您的浏览器缩放。我的不知何故是 125%,所以每四条 1px 的线画 2px 宽。

I spent hours trying to figure out why every fiddle on the internet worked and mine didn't (the zoom was only set for my dev tab)

我花了几个小时试图弄清楚为什么互联网上的每个小提琴都有效而我的却没有(缩放仅针对我的开发选项卡设置)

回答by Ghislain

The fillRect() method can be used to draw thin horizontal or vertical lines in canvas (without having to apply the +0.5 shift on coordinates):

fillRect() 方法可用于在画布中绘制水平或垂直细线(无需在坐标上应用 +0.5 偏移):

this.fillRect(left, top, 1, height);
this.fillRect(left, top, width, 1);

And you can actually make the lines even thinner by just replacing this code by something like:

您实际上可以通过将这段代码替换为以下内容来使线条更细:

this.fillRect(left, top, 0.7, height);
this.fillRect(left, top, width, 0.7);

Lines will be thinner (tending to reach 1 pixel wide) but their color a bit attenuated.

线条会更细(趋于达到 1 像素宽),但它们的颜色会变淡。

-> working example

-> 工作示例

To be noted that if we set ctx.lineWidth=0.7 (for the classical beginPath/moveTo/lineTo/stroke sequence), it does not work on Chrome (0.7 and 1 are interpreted the same way). Thus an interest for this fillRect() method.

需要注意的是,如果我们设置 ctx.lineWidth=0.7(对于经典的 beginPath/moveTo/lineTo/stroke 序列),它在 Chrome 上不起作用(0.7 和 1 的解释方式相同)。因此对这个 fillRect() 方法感兴趣。