Javascript Graphics w/ Raphael

Doing nice graphics isn’t as easy as one would like (what is?), and while having an artistic bent would certainly be an asset, for those of us without much of one, the next best thing would be tools that make it relatively easy to make things that look relatively nice. Raphael, a JavaScript library, is just such a tool.

The overall approach is pretty straight-forward: you include the library from your webpage, create a canvas for your graphics to be draw into, then create and modify objects to appear in that canvas, all done programmatically using JavaScript. The example from the website is:

// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);

// Creates circle at x = 50, y = 40, with radius 10
var circle = paper.circle(50, 40, 10);

// Sets the fill attribute of the circle to red (#f00)
circle.attr("fill", "#f00");

// Sets the stroke attribute of the circle to white
circle.attr("stroke", "#fff");

What follows are notes/examples from work being done to port EWFE to be browser-based instead of a traditional application.

Placing the canvas

Instead of specifying coordinates for locating the canvas, you can specify the ID of the DOM element (typically a DIV) you wish the canvas to be placed in.

Drawing a ball w/ a highlight & shadow

This was (lightly) adapted from the ball demo on the Raphael webpage.

/* Draw a ball centered at (x,y) w/ radius r; if hue isn't provided, red is assumed */
function makeball (x, y, r, hue) {
	hue = hue || 0;
	return paper.set(
		// The shadow
		paper.ellipse(x, y + r - r / 5, r, r / 2).attr({fill: "rhsb(" + hue + ", 1, .75)-hsb(" + hue + ", 1, .85)", stroke: "none", opacity: 0}),
		// The ball
		paper.ellipse(x, y, r, r).attr({fill: "r(.5,.9)hsb(" + hue + ", 1, .95)-hsb(" + hue + ", .5, .85)", stroke: "none"}),
		// The highlight
		paper.ellipse(x, y, r - r / 5, r - r / 20).attr({stroke: "none", fill: "r(.5,.1)#ccc-#ccc", opacity: 0})
	);
};

Things to note:

Drawing a rotated ellipse w/ text

The goal here was to draw an ellipse around some text, and then rotate them together.

// Create our text object
var mod_txt = paper.text(xx, yy, txt).attr(attr);
// Get the bounding box around it
var bb = mod_txt.getBBox();
// Create the surrounding ellipse
var mod_rect = paper.ellipse(bb.x+bb.width/2, bb.y+bb.height/2, (bb.width+20)/2, (bb.height+20)/2).attr(rattr);
// We're done with the bounding box of the text; get the one for the ellipse
bb = mod_rect.getBBox();
// Rotations are normally done around the center of an object; we want to rotate around one side or the other
// So we find the center of the ellipse, and how far to traslate it horizontally to get to the rotation point we want
var rw = -bb.width/2;
var rx = bb.x + bb.width/2;
var ry = bb.y + bb.height/2;
// If our ellipse is one to rotate the other way, negate the translation direction
if (  module_locs[name].flip==1 ) {
	rw = -rw;
}
// This defines a transform string: a sequence of commands to be applied to an object.
// In our case, we need 2 commands: 
//	tX,Y: translate by X,Y
//	rA,X,Y: rotate by A degrees about (X,Y)
var xfrm = "t"+rw+",0r"+module_locs[name].angle+","+(rx-rw)+","+ ry;
mod_rect.transform( xfrm );
mod_txt.transform( xfrm );
// Finally, move the text so it appears in front of the ellipse
mod_txt.toFront();

Tooltips

We can create our own tooltip-like functionality by binding a function to the hover action of an object (standard JavaScript stuff), and then drawing a box similar to a speech-bubble around the text of the tip.

/* Draw (or hide) a popup for obj */
function m_popup( obj, up ) {
	if ( up == 0 ) {
		// Remove the tooltip
		popup.remove();
		popup_txt.remove();
		return;
	}
	var bb = obj.getBBox();
	// Put the tooltip just above the object
	popup_txt = paper.text(bb.x+bb.width+10, bb.y-bb.height/2, obj.tooltip).attr(attr);
	bb = popup_txt.getBBox();
	// Tootltip boundary is drawn as a sequence of commands:
	popup = paper.path( 
			// 'M'ove to the 'dent' in the bubble
			"M" + (bb.x-5) + " " + (bb.y+bb.height+5) +
			// 'v'ertically draw a line 5 pixels more than the height of the text
			"v" + -(bb.height+5) + 
			// 'h'orizontally draw a line 10 more than the text's width
			"h" + (bb.width+10) +
			// 'v'ertically draw a line to the bottom of the text
			"v" + bb.height + 
			// 'h'orizontally draw a line so we're 5 pixels fro thge left side
			"h" + -(bb.width+5) +
			// 'Z' closes the figure
			"Z").attr( {fill:"45-#00ffff-#ffffff"} );
	// Put the text in front of the border
	popup_txt.toFront();
}

Note the fill: it is a gradient between the two colors specified, and drawn at a 45-degree angle.

Animations

There are some really impressive animations in the demos; I haven’t had much need to use animations, but something as simple as having an object pulsate is pretty easy to implement:

// Create an animation, in this case:
//  transition our object to being transparent
//	take 3000 milliseconds, 
//	"easing" in-and-out (this can be used to specify "rubber-band" and "elastic" effects for
//		the start and end of an animation, which are more dramatic when an animation involves movement
//	using closeHeartbeat as a callback when finished
var ao = Raphael.animation( {opacity:0}, 3000, "<>", function(){ closeHeartbeat(this); } );
// Have it repeat 10 times
ao = ao.repeat(10);
// Make it so!
object.animate( ao );
...
// Make our object visible when an animation is done
function closeHeartbeat(target) {
	target.attr({opacity: 100});
}

A post on graphics without pictures?!?

Certainly not!

NEWFE graph

Learning by Playing

There is a website of Raphael demos, which not only gives just about each method its own demo, but runs the code from a textarea which can be edited, which means you can test out variations on the provided demos (or just write your own) without having to deal with the admittedly minimal overhead of setting up your own page. It’s a little garish (wouldn’t be my choice of color scheme), but very handy for getting the feel of things.

Scott


Related Posts

blog comments powered by