inicio mail me! sindicaci;ón

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.

  • This is an interesting post. When I tried to research JavaScript 3D graphics capeabilites, all I found were some attempts to draw using div elements.

    I will have to read through your post again (since it has a too much information for a quick scan!), but do you have any links to simple examples? I clicked on the 'here is a copy' link and found something that I did not know how to use. :)

    How is the performance? (the 3D using div elements was unusable beyond a non-filled cube)

    How is the cross browser support?

    Thanks for your blog post about OperLazlo on my blog. If you get a chance, take a look at my survey of Ajax/Web Application technologies. There are several JavaScript libraries listed, and I would welcome informed input.

    ...Mike
    http://bear-webtech.blogspot.com/
  • Hey mike,

    Performance is interesting. Obviously it starts to choke when you have a lot of objects in the scene. I actually bumped into this early on, and changed the code so that it only redraws when an input event has been dispatched. Still, even then it can get quite chuggy. I'd imagine if i re-did it all in Flash it would be *much* faster.

    Cross browser support isn't bad, as every browser which supports the WhatWG canvas supports it well (with the exception of issues with Apple's Safari). Sadly Internet Explorer is a bit of a lost cause at the moment, though i have seen a few promising workarounds which use Flash as a replacement (which sadly defeats the whole point).

    Onto the mini howto...

    Both versions of the JavaScript draw tool i posted follow the same control scheme - you basically select objects in the canvas (which has a dashed border to let you see where it is) and drag them about.

    If you want to edit the points on an object, you select it and then click on the "Point Mode" (or "PM") button. You can then move the points around, etc. You can then re-enter the normal mode by pressing the "Object Mode" (or "OM") button.

    If you want to make a new object, there are a few links in the first version of the JSDT to create things such as line's, which work on a click & drag principle. The latter version of the JSDT has none of these functions (yet), except for the "TEST" button which adds a square polygon in the top left hand corner.

    Objects in both versions can be selected and dragged using the mouse. Multi-selection works by holding SHIFT down when selecting (though this doesn't work in all browsers since the key doesn't seem to be exposed).

    Objects in the latter version can also be rotated an scaled, using the "ROT" and "SCL" buttons. For the rotate, select your object and then drag to the left or right. For the scale, select your object then drag the little blue box which appears. Press the arrow button to go back into selection mode again.

    Another feature is the ability to group objects. The best implementation of this is in the latter version. To group objects, select them and press the "Group Objects" (or "GRP") button. To un-group object's, just click on the "Destroy Object" (or "DEL") button.
    Finally, when you have grouped objects, you can enter the group by double clicking on it. This will cause all interaction (including the addition of objects) to occur within the objects list of the group, rather than the global objects list. To go back to the main object list, just double click on an empty space. You can pretty much create an infinite hierarchy of groups-within-groups by exploiting this feature.

    Thanks for the feedback.
  • Michel Hegeraat
    I'm trying to show a layer2 map of connected devices.

    I figured out a way to do this creating a div for each line and device basically allowing me to update these individually using a ajax poll of a page that can reports any changes.

    I found that when you have many div's it seems to be more and more time consuming to access the div. e.g. if we have 10 device then moving a device while redrawing the lines takes half a second. If we have 200 devices then moving a device while redrawing the same 2 lines takes 10 seconds.

    I guess this shows that the browser has to search the div in the current document. I already started using an array to hold the reference to a div but this doesn't change anything. It' is like if the browser has to sequentially go through the source to find the right div.

    I guess I need something like SVG to get the performance right.

    Would there be any performance gain in using an object oriented style of javascript? I guess not as there is no way to associate the contents of a dom object with the property of a line or device object.

    I wonder what the canvas approach would add and how easy or difficult it would be to trap events like drag drop and click.

    Is line drawing faster when no div's are used?

    Would it be possible to see you source? The files in the example are not very readable and I guess I like source especially when there comments hinting me what each part is doing ;-)

    If you are interested is my sources feel free.

    Cheers,

    Michel
blog comments powered by Disqus