在画布上绘制一条 1px 粗的线会创建一条 2px 粗的线

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

提问人:CafeHey 提问时间:12/14/2012 最后编辑:gmanCafeHey 更新时间:6/16/2023 访问量:46010

问:

在这个 jsfiddle 中,有一行的 lineWidth 为 1。

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

例如:

ctx.lineWidth = 1;

但是,在画布上绘制时,线条的粗度为 2px,如何创建 1px 粗的线条。

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

谢谢!

JavaScript HTML HTML5-canvas

评论

3赞 Muhammad Talha Akbar 12/14/2012
嘿,我从未使用过画布,但如果在 1px 上它给出 2px 的输出,那么您可以尝试 1/2px 来获得 1px 高!

答:

3赞 RobIII 12/14/2012 #1

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

评论

0赞 CafeHey 12/14/2012
谢谢,我确实看到了,但我认为可能有更好的方法,这可能只是混淆了事情。我同意,半像素对我来说也有点奇怪!
118赞 Ferry Kobus 12/14/2012 #2

Canvas 从像素的一半开始计算

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

所以从一半开始就可以解决它。

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

ctx.lineWidth = 1;

// linear gradient from start to end of line
var grad = ctx.createLinearGradient(50, 150, 150, 150);
grad.addColorStop(0, "red");
grad.addColorStop(1, "green");

ctx.strokeStyle = grad;

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

ctx.stroke();
<canvas id="canvas1" width="500" height="500"></canvas>http://jsfiddle.net/9bMPD/http://jsfiddle.net/9bMPD/

这个答案解释了为什么它以这种方式工作。

评论

1赞 Bludwarf 5/5/2015
当您意识到应该在编码之前花时间阅读文档时。非常感谢@FerryKobus
4赞 Manngo 8/18/2018
在2018年对我有用。这是一个无需进行调整的简单修复:将所有内容偏移半个像素。完成需要做的事情后,恢复偏移量。context.translate(.5,.5);context.setTransform(1, 0, 0, 1, 0, 0);
5赞 Jelle Blaauw 10/1/2018
谢谢你的链接。我按以下方式使用它:其中删除了所有小数点,并且,它增加了半个像素,因此该行始终对齐良好。有了这个,我也可以将我的设置为 1。ctx.moveTo(~~x + .5, ~~y + .5);~~+ .5lineWidth
9赞 tonycoupland 12/14/2012 #3

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

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

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

评论

0赞 Ievgen 7/12/2019
使用像素比的一半而不是 0.5 来支持所有分辨率。
39赞 Richard 12/15/2012 #4

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

context.translate(0.5, 0.5)

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

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

您可以在此处阅读有关翻译功能及其使用方法的信息:

https://www.rgraph.net/canvas/reference/translate.html

这个答案解释了为什么它以这种方式工作。

评论

2赞 magritte 10/23/2015
这是一个被低估的答案,谢谢分享!
0赞 Pacerier 8/20/2016
@Richard,哇,这真是天才。这种方法有什么缺点吗?
0赞 Richard 8/20/2016
我不知道。这就是我在 RGraph 库 ( www.rgraph.net ) 中使用的。如果你进行转换,你必须再次翻译,这有点烦人——但我想它们并不经常完成。
1赞 Ievgen 7/12/2019
是的,但对于某些屏幕来说,0.5 不是像素的大小。您应该使用像素比。
4赞 Tom Ah 7/18/2017 #5

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/

评论

2赞 brad 2/17/2020
效果很好,但圆角要复杂得多;)
1赞 notak 2/1/2022
从字面上看,这根本行不通。当您将 0.5 添加到 x 或 y 值时,您会遇到同样的问题。
1赞 Ghislain 7/23/2017 #6

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

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

实际上,你可以通过用如下代码替换这段代码来使行更细:

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

线条会更细(倾向于达到 1 像素宽),但它们的颜色会有所减弱。

->工作实例

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

评论

0赞 Ievgen 7/12/2019
它也不锋利。使用笔画矩形,你一定会看到它。
1赞 Curtis 5/22/2018 #7

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

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

6赞 Ievgen 7/12/2019 #8

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

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

    像素比 = window.devicePixelRatio/ctx.backingStorePixelRatio

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

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

    context.lineWidth = 厚度 * 像素比;

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

    x = x + 像素比/2;

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

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 事件不会在截图中触发,因此您可以在 github 上尝试该文件