Skip to content

Instantly share code, notes, and snippets.

@getify
Created June 30, 2012 04:04
Show Gist options
  • Save getify/3022194 to your computer and use it in GitHub Desktop.
Save getify/3022194 to your computer and use it in GitHub Desktop.
polyfill for dashed-lines in canvas
// note: uses the native "mozDash" or "webkitLineDash" functionality if present, polyfills with manual dashed-lines drawing
// note #2: this polyfill does *not* currently work with arcs, bezier-curves, and quadratic-curves. that functionality would be theoretically possible, but is probably very hard to implement.
// note #3: it also doesn't work with rect() or strokeRect() yet, though those should be relatively straightforward to do, by manually drawing the lines using lineTo().
(function(){
var _moveTo = CanvasRenderingContext2D.prototype.moveTo,
_lineTo = CanvasRenderingContext2D.prototype.lineTo,
pos = {
x: 0,
y: 0
}
;
CanvasRenderingContext2D.prototype.moveTo = function(x,y) {
_moveTo.call(this,x,y);
pos.x = x;
pos.y = y;
};
CanvasRenderingContext2D.prototype.lineTo = function(x,y) {
// adapted from: http://stackoverflow.com/a/4663129
if ("_lineDash" in this && this._lineDash) {
_moveTo.call(this,pos.x,pos.y);
this.save();
var _x = pos.x;
var _y = pos.y;
var dx = (x-_x), dy = (y-_y);
var len = Math.sqrt(dx*dx + dy*dy);
var rot = Math.atan2(dy,dx);
this.translate(_x,_y);
_moveTo.call(this,0,0);
this.rotate(rot);
var dc = this._lineDash.length;
var di = 0, draw = true;
_x = 0;
while (len > _x) {
_x += this._lineDash[di++ % dc];
if (_x > len) _x = len;
draw ? _lineTo.call(this,_x,0) : _moveTo.call(this,_x,0);
draw = !draw;
}
this.restore();
this.moveTo(x,y);
}
else {
_lineTo.call(this,x,y);
pos.x = x;
pos.y = y;
}
};
})();
<!DOCTYPE html>
<html>
<head>
<script src="canvas-dashed-lines.js"></script>
</head>
<body>
<canvas id="foobar" width="500" height="500"></canvas>
<script>
var foobar = document.getElementById("foobar");
var ctx = foobar.getContext("2d");
with (ctx) {
strokeStyle = "#f00";
lineWidth = "1";
if ("mozDash" in ctx) {
mozDash = [10,5];
}
else if ("webkitLineDash" in ctx) {
webkitLineDash = [10,5];
}
else {
ctx._lineDash = [10,5]; // polyfill setting
}
beginPath();
moveTo(10,10);
lineTo(78,132);
lineTo(250,50);
lineTo(250,100);
lineTo(240,100);
stroke();
}
</script>
</body>
</html>
@danschumann
Copy link

What about doing a save and restore that would put the _lineDash value somewhere during save and restore it

@danschumann
Copy link

var _save = CanvasRenderingContext2D.prototype.save,
  _restore = CanvasRenderingContext2D.prototype.restore;

CanvasRenderingContext2D.prototype.save = function() {
  this._savedLineDash = this._lineDash
  _save.apply(this, arguments);
};
CanvasRenderingContext2D.prototype.restore = function() {
  this._lineDash = this._savedLineDash;
  _restore.apply(this, arguments);
};

@danschumann
Copy link

Also, it doesn't work great if all of your line segments are like 1 px long. The lineTo shouldn't default to draw = true. Just because you're calling lineTo doesn't mean that you need to draw, because you might be in a gap. If you have a lineDash of 5px on, 5px off, and you draw 10 line segments, each 1px each, then the first 5 should be filled and the next 5 not... I'll see what I can come up with.

@danschumann
Copy link

Here's one that allows you to draw hundreds of 1px line segments. It keeps track of the 'dashedness' around corners

https://gist.github.com/danschumann/ec0745f9e1889c0c6e9d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment