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

前端 未结 8 1771
抹茶落季
抹茶落季 2020-12-08 04:01

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

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

e.g:

ctx.lineWidth = 1;

How

相关标签:
8条回答
  • 2020-12-08 04:17

    Canvas calculates from the half of a pixel

    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/

    This answer explains why it works that way.

    0 讨论(0)
  • 2020-12-08 04:19

    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:

    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/

    0 讨论(0)
  • 2020-12-08 04:24

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

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

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

    0 讨论(0)
  • 2020-12-08 04:30

    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.

    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)

    0 讨论(0)
  • 2020-12-08 04:30

    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):

    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.

    -> 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.

    0 讨论(0)
  • 2020-12-08 04:36

    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;

    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

    0 讨论(0)
提交回复
热议问题