56

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

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

例如:

ctx.lineWidth = 1;

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

我可以画一个矩形(高度为 1px),但是我希望这条线也可以在对角线上工作。那么如何让这条线高 1px?

谢谢!

4

8 回答 8

114

Canvas 从半个像素计算

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

所以从一半开始会解决它

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

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

于 2012-12-14T13:14:34.340 回答
37

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

context.translate(0.5, 0.5)

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

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

您可以在此处阅读有关翻译功能以及如何使用它的信息:

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

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

于 2012-12-14T18:36:47.903 回答
8

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

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

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

于 2012-12-14T13:15:58.593 回答
6

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

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

    pixelRatio = window.devicePixelRatio/ctx.backingStorePixelRatio

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

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

    context.lineWidth = 厚度 * pixelRatio;

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

于 2019-07-11T22:00:57.073 回答
4

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

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/

于 2017-07-18T09:45:41.577 回答
3

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

于 2012-12-14T13:14:08.963 回答
1

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

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

于 2018-05-21T23:58:50.817 回答
0

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() 方法很感兴趣。

于 2017-07-23T13:36:06.800 回答