Saturday, August 6, 2011

Dashed Lines in HTML5 Canvas

There is a lot of functionality missing from the Canvas API. One obvious facility is support for dashed lines. Here is the start of some dashed line code I am working on as part of a larger project. It is not fully tested yet so caveat emptor. I started with code I found here and attempted to make it more efficient by taking most of the math out of the main loop.


var initDashing = function(ctx) {
if (!ctx.dashLine) {
ctx.dashStyle=[3,2] ;
ctx.dashedLine = function(x1,y1,x2,y2) {
var dashStyle = ctx.dashStyle,
dashCount = dashStyle.length,
sign = x2>=x1 ? 1 : -1,
dx = x2-x1,
dy = y2-y1,
m = dy/dx,
xsteps = dashStyle.map(function(len){return sign*Math.sqrt((len*len)/(1 + (m*m)));}),
dRem = Math.sqrt( dx*dx + dy*dy ),
dIndex=0,
draw=true;
this.moveTo(x1,y1) ;
while (dRem>=0.1){
var dLen = dashStyle[dIndex],
xStep = xsteps[dIndex];
if (dLen > dRem) {
xStep = Math.sqrt(dRem*dRem/(1+m*m));
}
x1 += xStep ;
y1 += m*xStep;
this[draw ? 'lineTo' : 'moveTo'](x1,y1);
dRem -= dLen;
draw = !draw;
dIndex = (dIndex+1) % dashCount ;
}
};
};
};


Here are some examples of usage (here I am using the Jasmine unit testing framework)



describe("Dashing", function() {
it("draws a 3-2 dashed line by default", function(){
var ctx = document.getElementById('test1').getContext("2d");
ctx.strokeStyle = "black" ;
initDashing(ctx) ;
ctx.beginPath() ;
ctx.dashedLine(0,0,100,100) ;
ctx.closePath();
ctx.stroke();
});

it("draws a 10-5 dashed line", function(){
var ctx = document.getElementById('test2').getContext("2d");
ctx.strokeStyle = "black" ;
initDashing(ctx) ;
ctx.dashStyle = [10,5] ;
ctx.beginPath() ;
ctx.dashedLine(0,0,100,100) ;
ctx.closePath();
ctx.stroke();
});

it("draws a 10-5-2-5 dashed line", function(){
var ctx = document.getElementById('test3').getContext("2d");
ctx.strokeStyle = "black" ;
initDashing(ctx) ;
ctx.dashStyle = [10,5,2,5] ;
ctx.beginPath() ;
ctx.dashedLine(0,0,100,100) ;
ctx.closePath();
ctx.stroke();
});

it("draws a 10-5-3-4 dashed line", function(){
var ctx = document.getElementById('test4').getContext("2d");
ctx.strokeStyle = "black" ;
initDashing(ctx) ;
ctx.dashStyle = [10,5,3,4] ;
ctx.beginPath() ;
ctx.dashedLine(100,13,0,80) ;
ctx.closePath();
ctx.stroke();
});
});







I still have to implement a dashed version of arc, encapsulate better and test this code much more thoroughly. I'll upload to git hub when it is ready.

1 comment:

Sal Mangano said...

This solution is broken for vertical lines. The Stack overflow link has a better solution that uses transformations.