Javascript Draw

Quite a while ago when i embarked on the Javascript Bandwagon, one of the first things i implemented was a rather simple draw test which plotted a few rectangles and other primitives on a WhatWG canvas object.

One of the little problems i encountered with the WhatWG canvas was that you really needed to play around with the values for the control points on such things as arc’s and bezier curves in order to get the result you wanted. Either that, or draw everything in a vector based drawing tool and convert it to the appropriate set of drawing commands to feed into the canvas object.

Safe to say, i wasn’t so easily defeated by this little problem, and so i had a crazy idea: would it be possible to create a drawing tool written entirely in javascript which used the canvas object?

The first implementation of the JavaScript drawing tool (which i will now refer to as JSDT) resembled frankenstein on steroids. I didn’t completely understand how JavaScript’s “classes” worked, so it was more or less a bunch of hap-hazardly designed functions all placed in the global namespace. Whilst it worked, it was hell to maintain, plus it lacked crucial features such as the ability to rotate and scale objects – as well as group them hierarchically.

For reference, here is a copy of the first version for you to laugh at. Note though that everything (including the manipulators) is handled in the little WhatWG canvas – no DOM in sight!

For the second version i decided to use classes from the ground-up, and make use of one of the various javascript toolkit’s available. In the end i chose Mootools, on account of it being lightweight and seemingly faster than the other framework’s i tried. I also planned out the design from the beginning, which helped a lot.

The second version sadly ended up being the third, as half-way through i realized that i wasn’t using the mootools “Class” object correctly. In error, i assumed attaching function’s to Classes was a bit of a memory intensive operation, and thus i decided to eliminate functions from the JSDT’s canvas objects, and instead place them in an “ObjectHelper” class.

Soon after, i realized how stupid i was, considering the functions are actually assigned to the object’s prototype – not its instance – and thus would be shared between each instance of the class. And thus, “ObjectHelper” was obliterated. A good thing too, as i noticed a big speed boost! I guess it just goes to show how much slower it is to use “this.object_helper.do_that(object);” rather than the more concise “object.do_that();”.

For reference, here is a copy of the third version. Its much improved over the first, and you can even change the selected object’s style properties via the nice toolbox. Sadly, i haven’t yet got round to implementing the “Save” and “Load” functions, which would be the next major step in implementing the JSDT – shouldn’t be too hard to write out the canvas object list as JSON and send it to / from the server.

Safe to say, i learned a lot about the peculiarities of JavaScript when making the various iterations of the JSDT. I also experienced first hand how utterly annoying it is to have code work perfectly in one browser, yet fail miserably in the other. One interesting issue i did find with regard to the WhatWG canvas was that Apple’s Safari browser couldn’t quite handle this code:

...
canvas.moveTo(10,10);
canvas.beginPath();
canvas.lineTo(20, 10);
canvas.lineTo(20, 20);
canvas.lineTo(10, 20);
canvas.closePath();
canvas.fill();
canvas.stroke();
...

In Firefox, Mozilla, and Opera this would draw a little filled in square with a line border. In Safari however, it draws a filled in square without a line border! It seems that Safari completely forgets about the path after you either fill or stroke it, so the only workaround is to just plot the path again, i.e.:

...
canvas.moveTo(10,10);
canvas.beginPath();
canvas.lineTo(20, 10);
canvas.lineTo(20, 20);
canvas.lineTo(10, 20);
canvas.closePath();
canvas.fill();
// +++
canvas.beginPath();
canvas.lineTo(20, 10);
canvas.lineTo(20, 20);
canvas.lineTo(10, 20);
canvas.closePath();
// +++
canvas.stroke();
...

…which as you can imagine is much slower, especially if you are drawing lots of primitives!

For my next rewrite of the JSDT, i think i might look at implementing it in an abstraction layer such as OpenLazlo or the Google Web Toolkit. Hopefully then i will be able to eliminate much of the manual workarounds i have had to hack in, instead leaving it to a nice automated tool.