Complete Solution for Drawing 1 Pixel Line on HTML5 Canvas

前端 未结 2 1163
感情败类
感情败类 2021-02-06 08:38

Draw 1 pixel line on HTML5 canvas is always problematic.(Refer to http://jsbin.com/voqubexu/1/edit?js,output)

The approach to draw a vertical/horizontal line is x+0.5, y

相关标签:
2条回答
  • 2021-02-06 09:12

    The "wider" line you refer to results from anti-aliasing that's automatically done by the browser.

    Anti-aliasing is used to display a visually less jagged line.

    Short of drawing pixel-by-pixel, there's currently no way of disabling anti-aliasing drawn by the browser.

    You can use Bresenham's line algorithm to draw your line by setting individual pixels. Of course, setting individual pixels results in lesser performance.

    Here's example code and a Demo: http://jsfiddle.net/m1erickson/3j7hpng0/

    enter image description here

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <style>
        body{ background-color: ivory; }
        canvas{border:1px solid red;}
    </style>
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
        var data=imgData.data;
    
        bline(50,50,250,250);
        ctx.putImageData(imgData,0,0);
    
        function setPixel(x,y){
            var n=(y*canvas.width+x)*4;
            data[n]=255;
            data[n+1]=0;
            data[n+2]=0;
            data[n+3]=255;
        }
    
        // Refer to: http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#JavaScript
        function bline(x0, y0, x1, y1) {
          var dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
          var dy = Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1; 
          var err = (dx>dy ? dx : -dy)/2;        
          while (true) {
            setPixel(x0,y0);
            if (x0 === x1 && y0 === y1) break;
            var e2 = err;
            if (e2 > -dx) { err -= dy; x0 += sx; }
            if (e2 < dy) { err += dx; y0 += sy; }
          }
        }
    
    }); // end $(function(){});
    </script>
    </head>
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    
    0 讨论(0)
  • 2021-02-06 09:19

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

    1. Get and scale canvas with a 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.

    1. use image-rendering: pixelated;

    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(x1, y1, x2, y2) {
    
      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, x1), getSharpPixel(thickness, y1));
      context.lineTo(getSharpPixel(thickness, x2), getSharpPixel(thickness, y2));
      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,50,250,250);
      pixelPerfectLine(120,0,120,250);
      pixelPerfectLine(122,0,122,250);
      pixelPerfectRectangle(10, 11, 200.3, 43.2, 1, false);
      pixelPerfectRectangle(41, 42, 150.3, 43.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)
提交回复
热议问题