INFO-I400 Topics in Informatics

Graphics, Animation, and Multimedia for the Web

Lesson 20: Dynamic SVG

Prerequisites:

Reading Assignment

David Dailey, An SVG Primer for Today’s Browsers:

Learning Objectives

Be able to use JavaScript in combination with SVG to:

  1. Respond to user actions such as mouse events and HTML controls
  2. Insert, modify, and remove elements of the DOM tree
  3. Randomize SVG elements and attributes

Introduction

It should be obvious by now that SVG is pretty powerful. Graphical elements “know where they are” (we don’t have to keep redrawing them when the scene changes); they can be animated declaratively; they can even respond to events coming from other graphical objects.

What sort of things can’t we do with SVG, without resorting to scripting? Or what would at least be very difficult do manage with just plain SVG?

The <script> Element in SVG

SVG, as well as HTML, documents can contain <script> tags. In SVG they are a little different:

<script xlink:href="SCRIPT-URL"></script>

for an external script (using the xlink:href instead of src attribute); or

<script>
<![CDATA[
  ...
]]>
</script>

for an inline script. The CDATA section prevents our script from being parsed as XML. (Should we have been doing this also with HTML scripts to control the canvas?)

Alternatively, you can have a script in your HTML (not in the SVG), and it can still access the SVG subtree of the document.

Events in SVG

Any SVG graphical element (e.g., <rect> or <g>) can have attributes that specify an event handling function or script. Some of these attributes are onfocusin, onfocusout, onactivate, onclick, onmousedown, onmouseup, onmouseover, onmousemove, and onmouseout. Animation elements can have onbegin, onend, and onrepeat. There are also document events. SVG 1.1 does not include keyboard events, although these are planned for a later specification.1 For details, see SVG 1.1, section 16.2, Complete list of supported events and SVG 1.1, section 18.4, Event attributes.

For generality, allowing non-mouse devices as well as mice, onactivate may be preferable to onclick.

Event Attributes

Event objects (we will use the conventional variable evt) have a target (evt.target), the element which triggered the event (e.g., the rect that you mouse over). In case of complex (grouped) objects, you may use evt.currentTarget to find the group element to which the listener is assigned, where evt.target will be the component (e.g., rect or ellipse) within the group that actually triggered the event.

Mouse events have evt.clientX and evt.clientY. It looks like clientX and clientY are relative to the SVG element, as long as the script is an SVG script element instead of an HTML script element.

The evt.type is the type of event (e.g., "mouseover").

Namespaces

Recall that namespaces in XML, like modules in Python and packages in Java, allow us to use the same name in different contexts without conflict. Because XML is not so much a language as a framework for making languages, most XML “applications” (i.e., languages based on XML) define and use a namespace. An XML document can have a default namespace and can also use prefixes to represent explicit namespaces. For more on this, see INFO I308 notes on XML namespaces.

The Document Object Model (DOM)

Any HTML, SVG, or other XML document has (or in the case of HTML, should have) a tree structure consisting of a root node with subtrees. For more information on the DOM, see INFO I308 notes on the Document Object Model.

To make scripted changes in our document, we manipulate this tree.

Useful methods (use NS variants for namespace-qualified elements and attributes; also, null can be used if there is no namespace):

document.getElementById("ID") // returns a NODE

node.getElementsByTagNameNS("NAMESPACE", "TAGNAME") // returns a sequence of nodes

document.createElementNS("NAMESPACE", "TAGNAME") // returns a NODE

node.getAttribute("ATTRNAME") // returns a string

node.getAttributeNS("NAMESPACE", "ATTRNAME") // returns a string

node.setAttribute("ATTRNAME", "ATTRVALUE")

node.setAttributeNS("NAMESPACE", "ATTRNAME", "ATTRVALUE");

node.appendChild(CHILDNODE) // inserts CHILDNODE into the subtree of node
                            // as its last child

node.removeChild(CHILDNODE) // removes CHILDNODE from the subtree of node

Note: SVG elements are in the SVG namespace, "http://www.w3.org/2000/svg", but SVG attributes without a prefix are in no namespace. Therefore, to get an attribute value, either use the no-namespace version node.getAttribute("ATTRNAME"), or use null for the namespaces in node.getAttributeNS(null, "ATTRNAME"); similarly for setting attribute values.

Useful attributes for navigating through the DOM, from some node:

node.parentNode
node.firstChild
node.nextSibling
node.childNodes.length
node.childNodes.item(INDEX) // 0 <= INDEX < length

node.attributes // itemlist of objects having nodeName and nodeValue properties

node.nodeValue // e.g., the string value of a text node

Example 20-01

Random gradients. Click on an ellipse to change its gradient stops to random colors.

Here is the solution.

Comments:

Example 20-02

A particle system. A stream of orange-yellow particles flows from left to right, like fire blown by a dragon. This involves inserting animated ellipse elements, representing the particles, into the DOM.

Here is the solution.

Comments:

Additional JavaScript Notes

Here are some more methods that are sometimes useful:

node.getBBox()  // rectangular bounding box of the graphical element

// for <path> element nodes
pathnode.getTotalLength() // number
pathnode.getPointAtLength(DISTANCE) // point with x, y properties

// for <animate>, <animateTransform>, <animateMotion> element nodes
// with begin="indefinite" to inhibit starting before scripted begin
node.beginElement(); // starts animation
node.endElement(); // stops animation

// Methods of the SVG element; available as top-level functions
// within an SVG script, but must be called as methods of the
// SVG node in HTML scripts

// Get and set the time in seconds relative to the start of
// the SVG element, which is the 0 time for beginning animations.
svg.getCurrentTime();
svg.setCurrentTime(secs);

Example 20-03

Modify the particle system to start on a mouse click. The particle stream is activated in short bursts by mouse clicks.

Here is the solution.

Comments:

Pausing and Playing

Since animations can be CPU-intensive (up to nearly 100%), offering controls to pause and start the animation is courteous.

// stop or restart all animations
svg.pauseAnimations();
svg.unpauseAnimations();
svg.animationsPaused(); // boolean result

Example 20-04

Multiple animations with pause and restart controls in HTML. This is like Example 19-03, but with controls.

Reference


  1. In fact, the SVG2 draft still says, “the SVG specification does not provide a key event set. An event set designed for use with keyboard input devices will be included in a later version of the DOM and SVG specifications. SVG 2, Chapter 16, Interactivity What can we do, then—set the event handler on an enclosing HTML element, maybe a div or the whole document?↩︎