I love the <canvas> tag, but its 2D drawing API is intentionally minimal and unfriendly, so I've written a simple wrapper library that improves it.
canto.js defines a single canto() factory function. Pass a canvas element or the id of a canvas element to this function, and it returns a Canto object to you. This Canto object is a drawing context, like the one you'd get by calling getContext('2d') on the canvas element. The Canto object implements the 2D drawing API, so your existing canvas code should work as-is, but it also implements a number of other features.
One of the most important API improvements is that all Canto methods that do not have some other return value return the Canto object. This enables method chaining. For example:
// Draw a triangle
canto("canvas_id").moveTo(100,100).lineTo(200,200,100,200).closePath().stroke();
Notice that the lineTo() method in the code above has four arguments: this is an extension that allows the single method call to draw two line segments.
Another useful API extension is that methods that actually perform drawing operations (such as stroke(), drawImage(), fillText(), etc.) accept a set of graphics attributes to be used for that one operation.
// Draw a colorful triangle with wide lines
canto("canvas_id").moveTo(100,100).lineTo(200,200,100,200).closePath().
stroke({lineWidth: 15, strokeStyle: "red"});
If you've used SVG you may be familar with the compact path descriptions syntax used in the d attribute of the <path> element. Canto supports this syntax, both with an svgpath() method and with individual single-letter methods like M (moveto), L (lineto), C (curveto) and so on. Here's the SVG version of our triangle:
// Parse an SVG path string
canto("canvas_id").svgpath("M100 100 L200 200 100 200 Z").fill();
// Define path elements with methods with really short SVG-inspired names
canto("canvas_id").M(100,100).L(200,200,100,200).Z().stroke();
Other new API features include relative-coordinate methods like rmoveTo() and rlineTo() and a turtle graphics API. A summary of the API is in the long comment at the top of the canto.js source code.
I've set up a canto-js project at code.google.com and have released it under the MIT license. Comments, bug-reports and contributions are all welcome. Test cases would be particularly nice. I've written a few simple tests myself, and I've run Canto against Phillip Taylor's suite of canvas tests. (Results: in Firefox and Chrome, at least, Canto passes effectively all the tests that the underlying un-canto'ed 2D context pass).
Note that Canto currently uses getters and setters to handle graphics attributes: if you set the lineWidth property on your Canto object, the setter method delegates to the underlying 2D context. IE doesn't support getters and setters, so Canto does not currently work in that browser.




How would I change the font color when drawing text? I tried
canto("tag").fillText("hello", 50, 30).fill({fillStyle: "#ccffaa"});
but text is still black...
Todd,
Pass the attributes to fillText():
canto("tag").fillText("hello",50,30,{fillStyle:"#cfa"})
fillText() fills the text directly: it does not define a path that you can later call fill() or stroke on.
Hi David,
Thanks that worked great! Now i'm looking at loading an image.
I read your documentation above the drawImage function and thought it might be best if I load my images using the Image object API as follows:
var image = new Image();
image.onload = function() {
canto('tag').drawImage(image);
}
image.src = "http://example.com/myimage.png";
In firefox 3.6.x I see this error from firebug:
uncaught exception: [Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIDOMCanvasRenderingContext2D.drawImage]" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: http://localhost:5000/javascripts/canto-0.11.js :: drawImage :: line 1461" data: no]
Now looking at that location it's the call to the internal draw method, if I log the image passed in right before that it's an array with an image tag inside...
Okay this was actually pretty straightforward, the drawImage method takes more than 1 argument. The first is the image, next 2 are x, y, and next 2 are width,height
Does this extension solve the problem of images as objects?
Could I place multiple images on the canvas and for each one to be a seperate object with their own onclick events?
Todd,
The Canto API is based on and is a superset of the Canvas API,and my documentation for it kind of assumes that you're already familiar with the Canvas API. (I'll be documenting that API in detail in the next edition of my book, by the way.)
Chris,
No, I haven't addressed that problem, but I have been wondering whether it would be worth implementing some kind of layers or sprites API... Note that you can use standard DHTML and CSS positioning techniques to position or elements over a canvas.
Very nice work. I've been inspired to borrow the API and implement it for PHP and the Cairo extension:
http://github.com/mgdm/canto.php
I'm trying to use T and S and getting this errors in Firefox.
canto("canvas1").svgpath("M75 60 Q125 190 175 60 T240 60").stroke();
argument is not defined
check(argument, 0, 2, 2);
canto("canvas1").svgpath("M30 150 C30 20 150 150 150 20 S220 20 220 150").stroke();
An invalid or illegal string was specified" code: "12
this._.bezierCurveTo(cx1,cy1,cx2,cy2,x,y);
Michael:
That sounds cool! Thanks for letting me know.
Sven:
Oops. My tests had pretty poor coverage, didn't they? I've fixed the bugs in S(), s(), T() and t(), and I think they work correctly now.