HTML5 Canvas Dashed Lines

Benchmark created by ckozl on


Description

HTML5 Canvas Dashed Lines

Preparation HTML

<style>
canvas { 
  border: solid 1px black; 
  box-shadow: inset 0px 2px 13px #999; 
  margin:2px;
  border-radius: 5px; 
}
</style>
<canvas id="c1"></canvas>
<canvas id="c2"></canvas>
<canvas id="c3"></canvas>
<canvas id="c4"></canvas>
<script>
  CanvasRenderingContext2D.prototype.dashedLine1 = function(x0, y0, x1, y1, pattern) {
  
    var length = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
    var vector = {
      x: (x1 - x0) / length,
      y: (y1 - y0) / length
    };
    var dist = 0;
    var i = 0;
  
    pattern = pattern && pattern.length ? pattern : [4, 4];
    while (dist < length) {
      var dashLength = Math.min(pattern[i++ % pattern.length], length - dist);
      dist += dashLength;
  
      (i % 2) && this.moveTo(x0, y0);
  
      x0 += dashLength * vector.x;
      y0 += dashLength * vector.y;
  
      (i % 2) && this.lineTo(x0, y0);
    }
  };
  
  CanvasRenderingContext2D.prototype.dashedLine2 = function(x1, y1, x2, y2, dashLen) {
    if (dashLen == undefined) dashLen = 2;
  
    this.moveTo(x1, y1);
  
    var dX = x2 - x1;
    var dY = y2 - y1;
    var dashes = Math.floor(Math.sqrt(dX * dX + dY * dY) / dashLen);
    var dashX = dX / dashes;
    var dashY = dY / dashes;
  
    var q = 0;
    while (q++ < dashes) {
      x1 += dashX;
      y1 += dashY;
      this[q % 2 == 0 ? 'moveTo' : 'lineTo'](x1, y1);
    }
    this[q % 2 == 0 ? 'moveTo' : 'lineTo'](x2, y2);
  
  };
  
  CanvasRenderingContext2D.prototype.dashedLine3 = function(fromX, fromY, toX, toY, pattern) {
    var lt = function(a, b) {
        return a <= b;
        };
    var gt = function(a, b) {
        return a >= b;
        };
    var capmin = function(a, b) {
        return Math.min(a, b);
        };
    var capmax = function(a, b) {
        return Math.max(a, b);
        };
  
    var checkX = {
      thereYet: gt,
      cap: capmin
    };
    var checkY = {
      thereYet: gt,
      cap: capmin
    };
  
    if (fromY - toY > 0) {
      checkY.thereYet = lt;
      checkY.cap = capmax;
    }
    if (fromX - toX > 0) {
      checkX.thereYet = lt;
      checkX.cap = capmax;
    }
  
    this.moveTo(fromX, fromY);
    var offsetX = fromX;
    var offsetY = fromY;
    var idx = 0,
        dash = true;
  var ang = Math.atan2(toY - fromY, toX - fromX);
  var factx = Math.cos(ang);
  var facty = Math.sin(ang);
    while (!(checkX.thereYet(offsetX, toX) && checkY.thereYet(offsetY, toY))) {
      
      var len = pattern[idx];
  
      offsetX = checkX.cap(toX, offsetX + (factx * len));
      offsetY = checkY.cap(toY, offsetY + (facty * len));
  
      if (dash) this.lineTo(offsetX, offsetY);
      else this.moveTo(offsetX, offsetY);
  
      idx = (idx + 1) % pattern.length;
      dash = !dash;
    }
  };
  CanvasRenderingContext2D.prototype.dashedLine4 = function(x0, y0, x1, y1, pattern) {
  
    var length = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
    var vector = {
      x: (x1 - x0) / length,
      y: (y1 - y0) / length
    };
    var dist = 0;
    var i = 0;
  
    pattern = pattern && pattern.length ? pattern : [4, 4];
    while (dist < length) {
      var dashLength = Math.min(pattern[i++ % pattern.length], length - dist);
      var draw = (i % 2);
      dist += dashLength;
  
      draw && this.moveTo(x0, y0);
  
      x0 += dashLength * vector.x;
      y0 += dashLength * vector.y;
  
      draw && this.lineTo(x0, y0);
    }
  };
  
  var D = 450;
  var COUNT = 1000;
  var cnv1 = document.getElementById('c1');
  var cnv2 = document.getElementById('c2');
  var cnv3 = document.getElementById('c3');
  var cnv4 = document.getElementById('c4');
  cnv1.width = cnv1.height = D;
  cnv2.width = cnv2.height = D;
  cnv3.width = cnv3.height = D;
  cnv4.width = cnv4.height = D;
  var ctx1 = cnv1.getContext('2d');
  var ctx2 = cnv2.getContext('2d');
  var ctx3 = cnv3.getContext('2d');
  var ctx4 = cnv4.getContext('2d');
  var points = [];
  var patterns = [];
  
  var i, j;
  
  for (i = 0; i < COUNT; i++) {
    for (j = 0; j < 4; j++) {
      points.push(Math.random() * D);
    }
    for (j = 0; j < 2; j++) {
      patterns.push(Math.random() * 10 + 2);
    }
  }
  
  ui.benchmarks[0].setup = function() { ctx1.clearRect(0, 0, D, D) };
  ui.benchmarks[1].setup = function() { ctx2.clearRect(0, 0, D, D) };
  ui.benchmarks[2].setup = function() { ctx3.clearRect(0, 0, D, D) };
  ui.benchmarks[3].setup = function() { ctx4.clearRect(0, 0, D, D) };
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
method #1
for (i = 0; i < COUNT; i++) {
  var x0 = points[i * 4 + 0],
      y0 = points[i * 4 + 1],
      x1 = points[i * 4 + 2],
      y1 = points[i * 4 + 3],
      pattern = [patterns[i * 2 + 0], patterns[i * 2 + 1]];
  ctx1.beginPath();
  ctx1.dashedLine1(x0, y0, x1, y1, pattern);
  ctx1.stroke();
}
ready
method #2
for (i = 0; i < COUNT; i++) {
  var x0 = points[i * 4 + 0],
      y0 = points[i * 4 + 1],
      x1 = points[i * 4 + 2],
      y1 = points[i * 4 + 3],
      pattern = [patterns[i * 2 + 0], patterns[i * 2 + 1]];
  ctx2.beginPath();
  ctx2.dashedLine2(x0, y0, x1, y1, pattern[0]);
  ctx2.stroke();
}
ready
method #3
for (i = 0; i < COUNT; i++) {
  var x0 = points[i * 4 + 0],
      y0 = points[i * 4 + 1],
      x1 = points[i * 4 + 2],
      y1 = points[i * 4 + 3],
      pattern = [patterns[i * 2 + 0], patterns[i * 2 + 1]];
  ctx3.beginPath();
  ctx3.dashedLine3(x0, y0, x1, y1, pattern);
  ctx3.stroke();
}
ready
method #1 (mod)
for (i = 0; i < COUNT; i++) {
  var x0 = points[i * 4 + 0],
      y0 = points[i * 4 + 1],
      x1 = points[i * 4 + 2],
      y1 = points[i * 4 + 3],
      pattern = [patterns[i * 2 + 0], patterns[i * 2 + 1]];
  ctx4.beginPath();
  ctx4.dashedLine4(x0, y0, x1, y1, pattern);
  ctx4.stroke();
}
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.