This entry is part 1 of 4 in the series Dive Into Dojo

Dojo GFX Vector Graphics

It’s well known that Dojo’s DojoX collection is a treasure trove of enterprise-ready, advanced widgets and JavaScript classes that allow you to accomplish difficult tasks with relative ease. ¬†One of DojoX’s most powerful features is GFX. ¬†Let’s dive into what GFX is, how to create feature-rich vector drawings with GFX, and how you can start creating vector graphics with GFX today!

What is GFX and Why Should Use It?

GFX is a cross-browser vector graphic creation library with support for SVG, VML, Silverlight, and Canvas.  GFX detects the best method for drawing by analyzing browser capabilities and using the best method for drawing;  you may also prioritize which drawing method should be used by GFX by defining a gfxRenderer setting:


GFX also relieves the developer and the user from needing an external plugin (i.e. Flash, Silverlight) to create and view vector graphics within the browser.  GFX graphics can respond to events and can even feature gradients!  What does that give us?  GFX allows you to create dynamic, beautiful, and data-driven graphics on the client-side with ease.

There are several possible pieces to a GFX vector graphic; ¬†let’s take a moment to learn about each piece.

Creating the Surface / Canvas

Like any great painting, a defined virtual canvas or “surface” must first be provided. ¬†The surface may be any size that you’d like and will be drawn within the provided node argument:

//assuming gfxNode is a DIV anywhere within the page
var surface = dojox.gfx.createSurface("gfxNode", 480, 480);

Note that besides the DOM node, you don’t need to know anything about which HTML tags to use or attributes to provide — GFX does the work for you!

Creating Shapes

GFX provides a host of defined, frequently used shapes: Rect, Circle, Ellipse, Line, Polyline, Image, Text, TextPath, and Path. ¬†The surface object provides quick methods for creating each of these shapes, so let’s look at the syntax to create each shape.

Rect

The Rect shape allows you to create rectangles and squares on the canvas:

surface.createRect({x: 100, y: 50, width: 200, height: 100 });

The x and y properties are the starting point for the shape; the width and height properties define the shape’s height and width.

Circle

The Circle shape allows you to create perfect circles on the canvas:

surface.createCircle({ cx: 100, cy: 100, r:50 });

The cx and cy properties represent where the coordinates of the circle’s center; ¬†r represents the circle’s radius.

Ellipse

The Ellipse shape allows you to create more flexible, customized ellipses:

surface.createCircle({ cx: 100, cy: 100, rx:50, r:25 });

The cx and cy properties represent where the coordinates of the ellipse’s center; ¬†rx and ry represent the horizontal and vertical radii.

Line

The Line shape allows for drawing a line from point A to point B:

surface.createLine({ x1: 100, y1: 50, x2:250, y2:90 });

The x1 and y1 properties represent the line’s starting point; x2 and y2 represent the line’s ending point.

PolyLine

The PolyLine shape allows for a line to be drawn from one point to several different points:

surface.createPolyline([{x: 250, y: 250}, {x: 300, y: 300}, {x: 250, y: 350}, {x: 200, y: 300}, {x: 250, y: 250}]);

The argument provided is an array of x and y-coordinate objects.

Image

The Image shape allows for loading of a bitmap images:

surface.createImage({width: 150, height: 100, src: "images/sitepen-logo.jpg"});

The x and y properties are the starting point for the shape; the width and height properties define the shape’s height and width. ¬†The src property must be provided which points to the image, just as an img tag would.

Text

The Text shape allows for creation of text:

surface.createText({ x:64, y:320, text:"My Text", align:"start"}).
setFont({ family:"Arial", size:"36pt", weight:"bold" }). //set font
setFill("blue");

The most important of the properties are the x and y coordinates as well as the initial text property.  A host of text styling and alignment properties may also be added.

TextPath

A TextPath is a shape that flows text along an arbitrary path. TextPath properties are based on the text shape properties.

// a curved text path!
surface.createTextPath({
	text: "Lorem ipsum dolor.",
	align: "middle"
}).
moveTo(0, 100).
setAbsoluteMode(false).
curveTo(CPD, 0, 100 - CPD,  300, 100,  300).
curveTo(CPD, 0, 100 - CPD, -300, 100, -300).
setFont({family: "times", size: "12pt"}).
setFill("blue");

TextPath accepts all parameters that Text does.

Path

A Path is the most versatile geometric shape, which can emulate all other geometric shapes:

surface.createPath("m100 100 100 0 0 100c0 50-50 50-100 0s-50-100 0-100z");

The string above is called a SVG Path Expression and represents coordinates within the canvas. GFX expertly reads and generates these strings where applicable.

As you can see, Dojo GFX makes drawing shapes incredibly easy. ¬†Let’s explore how visually styling these shapes is just as simple!

Styling Shapes

Drawing shapes is incredibly simple but they have no visual value without some type of fill or stroke.  Adding strokes and fills is just as easy as creating a shape:

//blue circle with yellow stroke
surface.createCircle({ cx: 100, cy: 100, rx:50, r:25 }).
setFill("blue").
setStroke("yellow");

//empty circle with rounded-dot, 3-pixel thick stroke
surface.createCircle({ cx: 100, cy: 100, rx:50, r:25 }).
setStroke({ style: "Dot", width: 3, cap: "round" });

Simple fills and strokes can be added in string name or hexadecimal format. ¬†Strokes and fills can be changed at any time with the setStroke and setFill methods. Simple fills are only the most basic of styles. ¬†GFX has long support linear gradients and recently added radial gradients. Linear gradients (which work from “top” to “bottom”) ¬†require x1, y1, x1, y2 coordinates, as well as a color parameter which represents an array of “stop” objects representing colors at specific parts of the the overall gradient:

surface.createRect({x: 100, y: 50, width: 200, height: 100 }).
setFill({ type:"linear", 
	x1:0, 				
	y1:0, 	//x: 0=>0, consistent gradient horizontally
	x2:0, 	//y: 0=>420, changing gradient vertically
	y2:420, 			
	colors: [{ 			//fades from white to black
		offset:0, 
		color:"#fff"
	}, { 
		offset:1, 
		color:"#000"
	}] 
});

Offset 0 is the “top” of the shape/gradient while offset 1 represents the “bottom” or “end” of the gradient.

Radial gradients (gradients that work from “inside out”) work very much like linear gradients in their assignment with the exception that coordinates are provided in cx, cy, and r formats:

surface.createEllipse({
	cx: 150,
	cy: 200,
	rx: 100,
	ry: 100
}).setFill(dojo.mixin({
	type: "radial",
	cx: 150,
	cy: 200
},{
	colors: [
		{ offset: 0,   color: [255, 255, 0, 0] },
		{ offset: 0.5, color: "orange" },
		{ offset: 1,   color: [255, 255, 0, 0] }
	]
});

GFX-created gradients render extremely well and offer much more visual depth than solid-fill elements.

Creating Groups

Creating shape “groups” ¬†can be extremely important when looking to animate or attach events to entire GFX graphics. ¬†Luckily group creation and adding shapes to groups is very simple:

/* create the surface */ 
var surface = dojox.gfx.createSurface("gfxNode", 480, 480); 

/* create the group */ 
var group = surface.createGroup();

/* add shapes on group instead of surface */
var rectShape = group.createRect({ x:0, y:0, width:200, height:100 }).setFill("#0000ae");

Shapes may also be added to a group after they’ve been created on the surface.

/* add shape on surface, then move to group */
var rectShape = group.createRect({ x:0, y:0, width:200, height:100 }).setFill("#0000ae");
group.add(rectShape);

Shapes can be added to a group or surface, then moved as needed!

GFX Animations!

One of the most amazing parts of GFX is the ability to animate shapes.  Shape animation classes live within the dojox.gfx.fx namespace and look very much like the Dojo Fx API in as far as class arguments.  dojox.gfx.fx classes include: animateFill, animateFont, animateStroke, and animateTransform.  A sample animateTranform class usage to rotate a shape (or group) 360 degrees would look like:

var fx = new dojox.gfx.fx.animateTransform({
    duration: 1200,	//1.2 seconds
    shape: myShape,	//our shape here
    transform: [{ 	//rotate 360 degrees at 240x240 (middle of 480/480 shape)
		name: "rotategAt",
		start: [0,240,240],
		end: [360,240,240] 
	}]
}).play();

Transforms can be in the form of rotations or translations. ¬†You may also transform a shape to “original”, which transforms the shape to its original state.

Sample fill and font animations would look like:

dojox.gfx.fx.animateFill{{
	shape: shape,
	duration: 500,
	color: {start: "red", end: "green"}
}).play();

dojox.gfx.fx.animateFont{{
	shape: shape,
	duration: 500,
	variant: {values: ["normal", "small-caps"]},
	size:  {end: 10, units: "pt"}
}).play();

One common place to use a fill animation would be activating and inactivating a button, which could be created by GFX.

Basic Scaling

Scaling of GFX is, yet again, quite simple. To double the height and width of a given graphic, you’d code:

//double
group.applyTransform(dojox.gfx.matrix.scale({x:2, y:2}));
//half
group.applyTransform(dojox.gfx.matrix.scale({x:0.5, y:0.5}));

The two examples above keep the same size ratio of the graphic. You are more than welcome to scale a graphic at any size you’d like.

GFX Events

A common theme among the Dojo Toolkit family is outstanding flexibility . ¬†The flexibility of dojo.connect method allows you to add event listeners to GFX shapes and groups. ¬†The shape.getEventSource method provides the “source element” of the event which is passed to the dojo.connect method:

dojo.connect(group.getEventSource(),"onclick",function() {
    //our shape was clicked, now do something!
});

The example above adds a click listener to the shape group which rotates the group 360 degrees when clicked. ¬†You may execute any functionality you’d like when an event is fired.

Tip:  Silverlight supports the following events: onclick, onmouseenter, onmouseleave, onmousedown, onmouseup, onmousemove, onkeydown, and onkeyup. If you want to target the broadest range of renderers, you are advised to restrict yourself to this list of events.

Draggable GFX Shapes

GFX shapes and groups may be also be made draggable:

new dojox.gfx.Moveable(group); // moveable, that's all!

The DojoX drawing library, a Photoshop-style vector graphic drawing suite, make heavy use of GFX’s draggable feature.

GFX Application:  Creating the London Ajax Logo

Dojo GFX Vector Graphics

Now that we’ve covered the basics of GFX graphic creation, we can create an animated, beautiful graphic. ¬†A simple graphic we can create is the London Ajax logo, for the popular user group (see video and slides at the end of this post). ¬†The first step in creating a graphic manually is visually identifying the different shapes within the static graphic you’re basing the soon-to-be-created GFX graphic. ¬†You can see the image is composed of:

  1. An upside-down “V” which makes for the “A” shape; ¬†gradient from one red to another red.
  2. A solid blue rectangle.
  3. Solid white text shape with “London AJAX” verbiage.

This image is fairly simple but let’s break it down by steps.

Tip:  To get the proper coordinates for each shape of the vector graphic, use a static graphic and grab the pixel dimensions/points.  Photoshop and comparable tools are extremely helpful.  Remember that you can scale the image larger or smaller using GFX, so a baseline is all you need.

Step 1:  Create the Surface and Group

As always the first step is creating the surface.  I want to create one graphic comprised of many shapes, so I will create a group right away:

/* create the surface */ 
var surface = dojox.gfx.createSurface("gfxNode", 480, 480); 

/* create the group */ 
var group = surface.createGroup();

Shapes will be added to this group instead of the overall surface.

Step 2:  Create the Upside-Down V

Since the upside-down V is not a standard shape, a Path shape will be the best option for creating this shape.

/* create v shape */ 
var vShape = group.createPolyline([ 
    { x:28, y:420 }, 
    { x:100, y:420 }, 
    { x:240, y:124 }, 
    { x:384, y:420 }, 
    { x:456, y:420 }, 
    { x:276, y:44 }, 
    { x:208, y:44 }, 
    { x:28, y:420 } 
]).setStroke({color:"#a70017"}).
setFill({ type:"linear", x1:0, y1:0, x2:0, y2:420, colors: [{ 
	offset:0, 
	color:"#f3001f"
},{ 
	offset:1, 
	color:"#a40016"
}] });

The initial requirement is creating the shape via createPath with the specified coordinate group.  Once the shape is created, I can add a stroke and create a linear gradation.

Step 3:  Create the Blue Rectangle

The blue rectangle is a standard shape, so I can create that using createRect:

/* create the blue box */ 
var rectShape = group.createRect({ x:32, y:272, width:412, height:64 }).setFill("#0000ae");

Again, note that the shape is being added to the group, not the surface.

Step 4: ¬†Create the “London Ajax” Text

The Text shape is the obvious choice to create the “London Ajax” text:

/* create the text */ 
var textShape = group.createText({ x:64, y:320, text:"LONDON AJAX", align:"start"}).
setFont({ family:"Arial", size:"36pt", weight:"bold" }).
setFill("#ffffff"); 

Tada!  The GFX logo has been created and the graphic is now scalable!

Step 5:  Fun!  Events and Animation

To add a bit of fun to the the graphic, let’s add a click event to the graphic which will rotate the graphic 360 degrees, flip the gradient, and change the rectangle color. ¬†When the animation is complete, the graphic will be returned to its original state.

/* rotate and style! */
var originalVShapeColor = vShape.getFill(), originalRectShapeColor = rectShape.getFill();
var fx = new dojox.gfx.fx.animateTransform({
    duration: 1200,
    shape: group,
    transform: [ { name: "rotategAt", start: [0,240,240], end: [360,240,240] }],
    onAnimate: function() {
        vShape.setFill({ 
			type:"linear", 
			x1:0, 
			y1:0, 
			x2:0, 
			y2:420, 
			colors: [{ offset:0, color:"#a40016"},{ offset:1, color:"#f3001f"}] });
        rectShape.setFill("#0daf24");
    },
    onEnd: function() {
        vShape.setFill(originalVShapeColor);
        rectShape.setFill(originalRectShapeColor);
    }
});

/* play transformation when clicked */ 
dojo.connect(group.getEventSource(),"onclick",function() {
    fx.play();
});

This animation is relatively simple but should give you a good representation of how you can assign events and animations to elements. ¬†DojoX’s charting library uses GFX events and animations for their Highlight, MoveSlice, and Magnify plugins.

View Resulting Graphic

 

Automated Graphic Creation

GFX features an XSL-based automated SVG parser which takes a static SVG graphic and turns it into a GFX graphic, allowing for size, color, and every other type of manipulation. I’ve covered this outstanding resource on my blog.

Who’s Using GFX?

Dojo’s powerful GFX solution has been used by many enterprise-level websites, most notably ESRI. ESRI has created a mapping system featuring Dojo’s GFX library; you may view an example here. Many clients use Dojo’s charting classes which are based on the GFX library. GFX’s power, stability, and flexibility have made it the perfect solution for creating beautiful vector graphics.

Tips for GFX Graphic Creation

A few tips for GFX graphic creation:

  • Internet Explorer’s native VML engine got progressively worse from IE6 to IE8. ¬†Silverlight may be the best option when manually setting the rendering priority.
  • If your drawing is not rendering properly in IE8, use the META tag method of emulating IE7:
    
    
  • Visit the GFX docs to learn about quirks caused by each browser.

Get Graphic with GFX!

GFX is an outstanding vector graphics library featuring what you’ve come to expect from the Dojo Toolkit: ¬†flexibility and dependability wrapped in a modular, well-written code structure. ¬†The powerful dojox.charting library uses GFX as its base, as does dojox.drawing and dojox.sketch. ¬†Vector graphic creation is a great way to avoid loading the same image at different sizes and create animatable graphics!

London Ajax Vector Graphics Event, October 2010

Great GFX Resources