In Part 1 of this article we examined Scalable Vector Graphics (SVG) as an XML-based image format. In Part 2, we investigate SVG's dynamic and interactive nature and delve into the scripting techniques that make SVG come to life.
To get started with SVG (and in order to view the examples in this article) you will need to download the SVG viewer from Adobe.
The fully functional examples shown in this article can be viewed here:
Figure A shows three frames from an animated button created with SVG. In the first frame, the button opacity has been reduced to 70 percent. The second frame shows the end result when the user moves the mouse over the button area. The animated effect triggered by the hovering mouse pointer increases the button's opacity gradually, until it is at its full opacity. The third frame displays the last frame of an animation triggered when the button is clicked.
The button consists of two rectangles and a text element. The first rectangle is the blue rectangle that is visible under the text. All of these elements are nested within an anchor tag. The SVG <a /> tag is similar to the <a> tag in HTML. Clicking the nested shape replaces the current page with the URL in the xlink:href attribute. The basic structure for the example in Figure A is as follows:
<a xlink:href="http://www.mysite.com" id="anchor">
<rect id="blueRect" />
<text id="whiteText">Click me</text>
<rect id="glassRect" style="fill-opacity:0" />
<set attributeName="fill" from="blue" to="red"
The <set> tag is used to change an element's attribute from one state to another. In the example above, the <set> tag is nested in a rectangle, which changes color from blue to red when the user moves the mouse over it.
To change an element's CSS properties, you use attributeType="CSS", like this:
<set attributeName="filter" attributeType="CSS"
to="url(#DropShadow)" begin="mouseover" end="mouseout"/>
The drop shadow effect in Figure A is accomplished with a combination of SVG filters. The feGaussianBlur creates the drop shadow's fuzziness, while the feOffset's dx and dy attributes determine the direction and depth of the drop shadow.
<feGaussianBlur stdDeviation="2" />
<feOffset in="blurredAlpha" dx="2" dy="2" />
Let's assume we want the button to behave consistently whether the user has moved the mouse over the text or the rectangle part of the button. The first solution is to include a second rectangle that has zero opacity. This invisible rectangle is like a sheet of glass placed over the button to catch animation events.
The second solution is to group all of the button's subelements together by nesting them inside an anchor tag and routing the events to subparts of the button. This <set> tag will change the text's color regardless of whether the user's mouse is over the button's rectangle or the text:
<set attributeName="fill" to="#d0f0f0"
The SVG standard provides a mechanism for using a mouse to start an animation. In the code snippet below, the animation starts two seconds after the rectangle is clicked and lasts for two seconds. The rectangle's opacity fades from full opacity to a fifth of its original value.
<animate attributeName="opacity" from="1" to="0.2"
begin="click + 2s" dur="2s" fill="freeze" />
Scripts can be used to achieve the same, or even more complex, fine-tuned effects. The simplest way to call a script is via the shape's onclick attribute.
<rect onclick=" startAnimation()" />
The startAnimation() function initializes animation variables and sets up the animation timer. The interval value refers to the number of milliseconds between each frame.
timerId = setInterval('doFrame()', interval);
The doFrame() function is called once per frame and is responsible for creating the animation effect. The rectangle's opacity and movement is animated by gradually changing the currentValue variable.
Manipulating the document
In Figure B, when the user clicks on a rectangle it is displayed in front of the other two rectangles. This example uses the appendChild() method to change the visual order of elements by changing their positions in the document object model (DOM). This is a useful technique for creating layered effects and tabbed widgets.
Figure C is an example of DOM manipulation, in which the user creates dynamic text by clicking the blue button. Use the createElement() method to create a new blank node, set the new node's attributes, and add the node to the DOM using appendChild().
node.setAttribute("id","text" + count);
The most recently added text element gets deleted when the user clicks one of the text elements. The code gets a handle on the correct element using getElementById(), then uses its parent's removeChild() method to delete the element.
object=svgdoc.getElementById("text" + count);
|Draw a line|
Figure D displays another nifty use for DOM manipulation. The user gets to draw lines by clicking, dragging, and then unclicking at the end of the line. The SVG document defines a line that is invisible initially because its start and end x and y values are equal to zero.
When the user first clicks the SVG, the line's x1 and y1 values are set to the mouse x and y values. When the user starts dragging the mouse, the x2 and y2 values are set to the current mouse x and y values. The line is redrawn dynamically as the user moves the mouse. When the user unclicks the mouse, the line is redrawn in its final position.
Text wrapping is potentially one of SVG's trickiest techniques. Figure E displays a solution in which text is wrapped when it reaches the end of a line. Check out this example by loading Example E into a browser. Simply move the mouse over the box and start typing. The text wraps when the end of the line is reached, or when the enter key is pressed.
Two methods create the text wrap functionality. The textEvent(evt) method handles the capturing of text as the user types on the keyboard, while newRow() moves to the next row in the sequence.
In the textEvent(evt) method, String.fromCharCode(key) captures the latest character typed by the user. The line containing obj.firstChild.setData(str) updates the current row with the latest string. Note that we call the firstChild() method to get a handle on the text, which is a child of the text node. Once we have a handle on the text, it is possible to use setData() to replace the text.
The key to this technique is to know when to wrap the text. This is accomplished by calculating the length of the text using obj.getComputedTextLength() and ensuring that the current text's length does not exceed the maximum width allowed per line of text.
Batik is an Apache XML subproject that provides a software platform for the generation, manipulation, and viewing of SVG documents. After downloading Batik from the Apache Web site, unzip it onto your file system.
Batik also has its own SVG browser, called Squiggle. To install Squiggle, navigate to the Batik directory on your system and type the following into the command line:
java -jar batik-squiggle.jar.
There are several useful modules bundled with Batik, including the rasterizer module, which allows for the automatic transformation from SVG format to JPEG or PNG. The following command line converts a directory containing SVG documents into JPEGs and places the resulting files into the output directory:
java -jar batik-rasterizer.jar -m image/jpeg -q 0.8 -d output/ input/*.svg
SVG adds a new level of interaction to the Internet and related applications. Because SVG files are small and portable, they are perfect for applications that need to transmit data to a user in a dynamic, graphical form.