Dev.Opera - Follow the standards, break the rulesDev.Opera - Follow the standards, break the rules

Login

Lost password?

SVG Evolution 2: Our First Steps Into SVG

Adding Some Functionality

While eye candy is always good to get some wow, let's start to work on some practical uses of SVG in the area of "image gallery". We'll start slow by adding a toolbar with some buttons to the main image (the preview image) in the photo gallery. This will allow us to get our hands dirty with some SVG scripting. We'll also get a chance to see how CSS can be used to style SVG elements.

Styling The Toolbar

The toolbar should be semi-transparent and appear right on top of the main image. First, here is the code for the toolbar:

<g id="options_panel" display="none" transform="translate(50,5)">
  <rect id="options_panel_bkgnd" x="0" y="0" width="120" height="30" />
  <polyline class="topleftborder" points="0,30 0,0 120,0"/>
  <polyline class="botrightborder" points="120,0 120,30 0,30"/>
</g>

We will add the "options_panel" group (<g>) element to the SVG main image group ("the_preview"). At this point, the options_panel simply contains a rectangle ("options_panel_bkgnd") with some polylines serving as borders to give the panel a 3D feel. We're going to use CSS to style the SVG elements by updating our external CSS file:

g#options_panel {
  fill-opacity: 0.5;
  stroke-opacity: 0.5;
}
rect#options_panel_bkgnd {
  fill: grey;
}
polyline.topleftborder {
  fill: none;
  stroke: white;
  stroke-width: 2;
}
polyline.botrightborder {
  fill: none;
  stroke: grey;
  stroke-width: 2;
}

In the CSS, we set the entire "options_panel" group's opacity (both fill and stroke) to 0.5. This means that all children will, by default cascade rules, inherit the opacity of their parent, meaning the entire panel will be semi-opaque. The fill colour of the background rectangle and the stroke of the polylines are also styled.

Note that to associate this external CSS file with the SVG content, we must add the following line to the XML prolog:

<?xml-stylesheet href="ex6-gallery.css" type="text/css"?>

Displaying The Toolbar

You may notice in the above SVG code that the options_panel group is initially not displayed (the display attribute is set to "none"). This is because we only want to make the toolbar visible when we mouse-over the main image. To do this, we add a JavaScript function and assign two event listeners to call it. Here is the SVG code with the JavaScript added:

<g id="the_preview" onmouseover="showOptions(true)" onmouseout="showOptions(false)">
  <image id="thePreviewImage" class="preview" x="0" y="0" width="512" height="384"/>
  <text transform="rotate(90, 10, 20)" fill="white" fill-opacity="0.4"
    x="10" y="20" font-size="30" font-family="Times">SVG Image Gallery</text>
  <g id="options_panel" display="none" transform="translate(50,5)">
    <rect id="options_panel_bkgnd" x="0" y="0" width="120" height="30" />
    <polyline class="topleftborder" points="0,30 0,0 120,0"/>
    <polyline class="botrightborder" points="120,0 120,30 0,30"/>

    <script><![CDATA[
      function showOptions(bShow) {
        var optionsPanel = document.getElementById("options_panel");
        if(optionsPanel) {
          optionsPanel.setAttributeNS(null, "display", (bShow ? "inline" : "none"));
        }
      }
    ]]></script>
  </g>
</g>
The empty toolbar
Figure 3 - The empty toolbar

The event handlers for the mouse-over and mouse-out events are set at "the_preview" group element. These event handlers call the showOptions() function which accepts a true/false argument. The argument dictates whether the "options_panel" element will be visible or not. Click here to observe this in action (you will have to move your mouse onto the main image to see the empty toolbar).

Note that I've put the JavaScript <script> element as a child of the "options_panel" element. This may give the false impression that the script statements belong to the "options_panel" element or only has scope to the "options_panel" element. This is not the case. Scripts in SVG operate just like scripts in HTML: Script statements are simply executed when they are first encountered in document order. The showOptions() function does not belong to the DOM object but exists in the global (or "window") scope. Placing <script> elements nested within other SVG elements might make sense for small applications to keep code local to the SVG elements they impact, but as your application grows, it will eventually make sense to move all JavaScript into a separate .js file and reference that file once within the SVG document.

Some Simple Buttons

Now we'll add our first buttons to the toolbar. Nothing fancy to begin with, we're going to use the power of SVG transformations to do some very basic things: Flipping the image vertically and horizontally.

Buttons on the control panel will just be <rect> elements that contain simple graphics, listen for mouse click events and pass control to JavaScript functions. We leave it as exercises to the reader for something fancier (including improved button graphics, mouseout/mouseover event listening with some visual effect, animated button images, etc). Here is the code for a basic button:

<g id="flipv_button" cursor="pointer" onclick="flipVert()" pointer-events="all">
  <title>Flip Image vertically</title>
  <-- Button graphics go here in a 25x25 coordinate space -->
  <script><![CDATA[
    function flipVert() {
      // ...
    }
  ]]></script>
</g>

We will use the template above and copy it for each button, then transform the <g> element by doing a translation for each button. Note that the <title> element is used to provide some description of what the button does. User agents should make this information available (i.e. in tooltip or in the status bar) when moused over.

For flipping the image, our functions will simply keep track of a global JavaScript variable, toggle it from 1 to -1 and then set the transform attribute with an appropriate value of scale():

<script><![CDATA[
  // Global variables
  var gnFlippedHoriz = 1;
  var gnFlippedVert = 1;
  function flipImage() {
    var scaleStr = "translate(256,192) scale(";
    scaleStr += gnFlippedHoriz;
    scaleStr += ",";
    scaleStr += gnFlippedVert;
    scaleStr += ") translate(-256,-192)";
    var img = document.getElementById("thePreviewImage");
    if(img) { img.setAttributeNS(null, "transform", scaleStr); }
  }
  function flipHoriz() {
    gnFlippedHoriz *= -1;
    flipImage();
  }
  function flipVert() {
    gnFlippedVert *= -1;
    flipImage();
  }
]]></script>

Look at what the flipImage() function does: A horizontal flip is accomplished by scale(-1,1), while a vertical flip is scale(1,-1). Since all transformations take place with respect to the origin (0,0) of the coordinate system, we need to first move the image so that its center point is at the origin (translate(-256,-192)), then scale the image, then move the image back so that its top-left point is at the origin (translate(256,192)). To flip the image horizontally (but not vertically), the transform attribute value should be "translate(256,192) scale(-1,1) translate(256,-192)".

Click Here to play with the final version of the code.

I hope this article has helped convinced you that SVG is a technology that can be used to provide both elegant interactivity and useful functionality to Rich Internet Applications in modern web browsers. In the next (and final) article in this series, we'll buff off the rough edges of our sample SVG+XHTML+CSS+JS web application, provide yet more useful functionality to the Image Gallery, and illustrate a couple more complex features of SVG.

digg Digg this article  del.icio.us Add to del.icio.us

Article categories