Getting Ready for SVG Open

I recently demonstrated Test Driving Modern SVG using the SVG Dice sample currently on the Internet Explorer 9 Test Drive site. While building this sample, I learned that both performance and interoperability for SVG are a subtle continuum and are not binary. This point resonated with me so much that I modified my presentation for this week’s SVG Open Conference entitled “The Future of SVG and HTML5” to include methods by which the SVG developer community can rally around to make SVG more interoperable.

Testing SVG vs. Test Driving SVG

SVG has its roots in a document format. The most popular use case today is the static document format. Complex engineering diagrams and other illustrations are well suited for SVG given their requirements for scalability, high fidelity printing, and portability.

With HTML5, the future of SVG is about the next generation of the interactive graphic web which exercises SVG in new ways. As a community, we need to think about how we test the SVG specification differently.

Testing SVG

The W3C SVG Test Suites attempt to “test” the ability to implement the spec, but as we learned in the working group, this is not enough to guarantee an interoperable set where a developer can use the same markup that works across all browsers. The SVG Test Suite is not intended to test conformance, but rather whether or not the spec can be implemented. From the W3C SVG Test Wiki:

“Our test suites are necessary, but not sufficient, to test conformance… Thus, representations of tested support is skewed toward the more complex features of SVG, and is not an accurate view of overall SVG support”

In other words, the existing test suite doesn’t test whether a browser conforms to the spec. To this end, we are working closely with the SVG Working Group to help round out these tests; in fact, there is an external effort to create an SVG DOM 1.1 conformance test by the SVG Interest Group. At the time of writing, IE9 passes 100% of these automated tests. In concert with the SVG Working Group, we are helping to resolve these interoperability issues by continuing to enhance the SVG Test Suite through regular contributions.

Additionally, there exists an imbalance between the number of requirements in the modules in the SVG Specification, and the number of tests in the SVG Test Suite that represent those requirements. The Internet Explorer team helps to address this imbalance through test asset contributions. But this is not enough. We have contributed 56 tests and expect to continue to deliver more over time.

Test Driving SVG

The better measure of conformance and more importantly interoperability was do develop more complex SVG for the web. My own experience in test driving SVG by developing the SVG Dice demo illustrates some of the places where we have more work to do as a community on interoperability of SVG.


var vid = document.getElementById(“myvid”);
if(vid && vid.canPlayType && !vid.canPlayType(“video/mp4”).match(/^(probably|maybe)$ /i)) {
vid.parentNode.insertBefore(vid.firstElementChild, vid);
vid.style.display = “none”;
}

The SVG specification contains over 2000 individual requirements. This is a large number in comparison to other web specifications and means that there is plenty of room for different interpretations. We strive in the SVG W3C Working Group to identify these differences. When I developed the SVG demo with the requirement that it works across browsers, my eyes were opened to some of the difficulties web developers face.

Most of the interoperability issues stem from a combination of the large number of requirements, the dependencies on other specifications, and the lack of significant content on the web to ensure the features are interpreted the same by both developer and browser vendors alike. SVG is relatively new ground for web developers and whereas HTML and CSS have enjoyed decades of refinement by a large scale of users, SVG has not yet had the same level of use and scrutiny.

Now that SVG is a part of HTML5, we expect to see traditional web developers learning new and better ways to improve experiences for their users using vector graphics. At the last SVG Face to Face meeting, the SVG Working Group assembled scenarios for SVG that provide for more targeted use cases for the next generation, graphical web. SVG plays an integral role here.

Comparing Implementations

I wrote the SVG Dice demo from my own understanding of the specification using Internet Explorer 9 and subsequently tested it in other browsers. In most cases where I ran into conflicting behavior, at least two browsers still agreed. Sometimes Internet Explorer’s behavior matched Chrome or Safari, and other times it matched Firefox or Opera. Because Internet Explorer is the most recent implementation, it benefits from an SVG Specification that is in last call where at least some of the ambiguities and conflicts have been resolved in the specification. Clearly there are more.

Something that caught me off guard was the impact of performance on my development effort.


var vid = document.getElementById(“myvid2”);
if(vid && vid.canPlayType && !vid.canPlayType(“video/mp4”).match(/^(probably|maybe)$ /i)) {
vid.parentNode.insertBefore(vid.firstElementChild, vid);
vid.style.display = “none”;
}

From a developer’s perspective, I wanted to use the same graphic features like opacity, gradients, and masks in all browsers, in order to provide a consistent interoperable user experience, but I couldn’t do that because of performance concerns. Fully hardware accelerated graphics on Windows helps to move these graphic intensive computations to the GPU. I added the “Low Fidelity” mode to enable users to experience this demo in browsers that don’t make full use of the GPU. One nice side effect is that this also demonstrates CSS styling of SVG.

The Surprising “Switching Event”

Debugging the differences between browsers caused a very interesting “switching event” for me as a developer. There were a few outstanding bugs in the IE9 debugging tools (now fixed) which prevented me from placing breakpoints in code when working with SVG, so I used the popular Firebug add-on for Firefox. However, running the demo on Firefox was too slow, so I reverted back to Internet Explorer 9 to debug.

I eventually found work-arounds for most of the incompatibilities without having to write browser specific code, but it required far more effort than expected or considered reasonable.  We have more work to do here as a community in building the promise of same markup for SVG.

The Code

Because SVG is an older spec but new to a lot of developers, I thought I would review some of the code and concepts in this demo up close.

The Document Structure

As a first example, most browsers do not yet support SVG in HTML5.  I had to structure the document as XHTML with inline SVG.

<!DOCTYPE html>
<html id="demohtml" xmlns="http://www.w3.org/1999/xhtml" class="testdrive">
<head>
<title>
SVG Dice
</title>
</head>
<body id="demobody" onload="Setup()">
<audio id="sndRemove" volume="1" src="assets/remove.mp3" preload="true" ></audio>
<svg overflow="visible" id="theSVG" xmlns=http://www.w3.org/2000/svg
xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" >
</svg>
</body>
</html>

The above simple <!DOCTYPE html> sets Internet Explorer 9 to run in “standards mode” where SVG is supported. Notice that the SVG is embedded directly into the HTML. Both the css and the script are linked in via the HTML elements as expected.

<script type="text/javascript" src="demo.js"></script>
<link rel="Stylesheet" type="text/css" href="demo.css" />

At this point I have my basic html and svg document, stylesheet, script file structures. Next I need to build the graphical elements, provide the styles and write the script necessary for the animations and user experience.

Adding the Content

One of the great benefits and unique nature of the web development community is what we called the “copy/paste” use case. Any developer can readily search Wikimedia for public domain SVG art and use these directly in their applications or on their sites. Other popular tools exist for generating new or modifying existing content, from the open source Inkscape which focuses specifically on SVG, to other graphic tools from Microsoft and Adobe which export to SVG. I used existing open source clip art for the dice and for the boat.

Initially I had kept these SVG sources in different files for the purpose of separation. Unfortunately, I ran into several different issues across browsers from sizing to SVG Images support, which forced me to include all of the SVG in the one HTML file.

SVG contains the concept of <defs>, which is used to define an element(s) with styles and attributes, that can be re <use>d later. These two concepts are very powerful. They allow me to create many different sized dice for use as the image on the cup, those that fall into the cup, or those that roll across the screen during game play. I organized the <defs> from the various files at the top document immediately after the SVG tag.

<defs>
<!-- for left die -->
<linearGradient id="grad21" x1=".445" x2=".554" y1=".442" y2=".555">
<stop stop-opacity=".9" offset="0" stop-color="#470808"/>
<stop stop-opacity=".9" offset=".65" stop-color="#700d0d"/>
<stop stop-opacity=".9" offset="1" stop-color="#8c1111"/>
</linearGradient>
</defs>

These <defs> are later referenced:

<!-- edges -->
<path d="M 2474 4434 L 4860 2945 C 5009 " fill="url(#grad21)"/>

Once the <defs> were in place and the main clipart content was inline, I designed the rest of the experience, keeping in mind that SVG renders in layers from top to bottom. The majority of the remaining content was the scoreboard, the felt, the text and the cup.

Adding the User Interactivity and Animations

One of the more challenging aspects of this demo was organizing the sizing. One of the first things I do in the code is register the resize event, and set up the dimensions of the <svg> and the contained svg fragments. For simplicity, I <g>rouped them so I can apply scaling and positioning individually.

document.getElementById("theSVG").setAttribute("width", surfaceWidth.toString() + "px");

Something to take notice of immediately here is that this JavaScript code is familiar to web developers because it is standard JavaScript working with the DOM. SVG has its own DOM methods which I use, but I mostly stick to DOM Level 2 constructs as there is general agreement in the SVG Working Group that the SVG DOM might need revisiting.

Next I needed to take both of the static dice as SVG Fragments, and create some code to clone them, store references in an array with properties that allow me to manipulate their transforms for the animation effects, as well as ensure the physics engine recognizes them accordingly. The loop to create the dice is straightforward; it loops through the number of dice desired, clones the original into the DOM and creates an array to manage them.

Note: Most code samples here are trimmed for brevity, but all of the code is on the Platform Preview site http://ie.microsoft.com/testdrive/Performance/SVGDice/Default.xhtml)

First I clone the prototype and add it to the DOM, setting some default attributes along the way. Note the establishment of the transform is used to move the dice (translate), rotate the dice (rotate) and size the dice (scale).

// create an instance of die #1 and add it to the SVG Doc
this.die1 = createElement("g");
var tmpChild = this.die1.appendChild(createElement("g"));
var tmpNode = this.die1.appendChild(tmpChild);
tmpNode.appendChild(nodeDie1.cloneNode(true));
// set some default attributes
this.die1.setAttribute("id", "die_1_" + number.toString());
this.die1.setAttribute("transform",
"translate(" + this.x.toString() + "," + this.y.toString() + ")
scale ("
+ this.scale.toString() + ")");

I wanted to make use of the <use> element here as this is a great way to clone a group of SVG fragments. Unfortunately, the implementation of <use> varies across browsers, specifically in the case of styling and events.

Next I use the SVGDOM getBBox() method to grab the dimensions of the die as we shrink each one along the way. This method returns an SVGRect which is used in the physics engine to detect collision.

var rectSize = this.die1.getBBox(); // calculate dimensions for use with physics
this.height = rectSize.height;
this.width = rectSize.width;

One of my favorite parts of building this sample was the discovery and use of the Box2DJS engine which made physics a breeze! This engine is used in many projects and is now available to web developers. Before I create the dice, I actually initialize the world around me:

function createWorld(width, height) {
var worldAABB = new b2AABB();

var world = new b2World(worldAABB, gravity, doSleep);
createGround(world, width, height);
// Side
createBox(world, 0, 0, 30, height);
createBox(world, width, 0, 30, height);
createBox(world, 0, 0, width + 30, 30);
createBox(world, 0,height , width+30,30);

return world;
}

And then during dice creation I add each die to the world and give it an initial velocity.

// add this dice to the physics engine
this.circleBody = createBall(world, this.xTrans, this.yTrans, this.width);
// give the force a slightly random starting velocity
this.circleBody.SetLinearVelocity(initialForce);

After adding some user interaction to add dice, I remove dice and shake the cup. Then, I create a timer and step the world.

timer = window.setInterval(DoStuff, 16);
// move the world
world.Step(timeStep, iteration);

The Die class has a prototype update function that is called when the world steps. The primary mechanism for moving the dice is to get the coordinates from the physics engine and set the transform property with all of the elements originally established:

var transFormString = "translate(" + Math.round(this.circleBody.m_position.x)
+ "," + Math.round(this.circleBody.m_position.y) + ") scale (" +
this.scale.toString() + ") rotate(" + Math.round(this.rotation).toString()
+ "," + Math.round(this.xTrans).toString() + "," +
Math.round(this.yTrans).toString() + ")";

We now have moving, rolling, colliding dice.

Note that using the transform attribute is not necessarily the fastest approach, depending upon the implementation. As mentioned previously, I shied away from the SVGDOM which provides methods such as setTranslate() and setRotate(). The method I chose here considers potential future use of CSS Transforms with CSS Transitions and/or CSS Animations.

Styling the Graphics

Lastly, I wanted to take advantage of SVG integrated into the DOM and use CSS to change the style of the scene. Since the original art came from a design tool, it contained RGB values for colors and opacities.

<linearGradient id="cgrad2c" x1="1" x2=".17" y1="0" y2=".58">
<stop stop-opacity=".9" offset="0" stop-color="#700d0d"/>
<stop stop-opacity=".9" offset="1" stop-color="#b51616"/>
</linearGradient>

I replaced these RGB values with styles:

<linearGradient id="cgrad2c" x1="1" x2=".17" y1="0" y2=".58">
<stop class="diceCorner6"/>
<stop class="diceCorner7">
</linearGradient>

This allowed me to create style sheets for each of these styles:

g#classHandler.vegas .diceCorner6 {stop-opacity:.9;offset:0;stop-color:#700d0d;}
g#classHandler.vegas .diceCorner6 {stop-opacity:.9;offset:1;stop-color:#b51616;}

g#classHandler.lowfidielity .diceCorner6 {offset:0;stop-color:#000000;}
g#classHandler.lowfidielity .diceCorner6 {offset:1;stop-color:#000000;}

And then set the one class property at the top of the document to change all of the styles in the document:

// set the overall stylesheet via class
document.getElementById("classHandler").setAttribute("class", style);

This allows the user to change the style sheet even while the dice are rolling as there is no difference to the DOM as to when these styles are changed. Hardware accelerated graphics in Internet Explorer 9 enable this to happen very quickly.

Call to Action

Start working with SVG in Internet Explorer 9. IE9 is platform complete for SVG in the latest platform preview release. Experiment with the feature set and tell us about incompatibilities or bugs you find using the “Report Issue” command and on Microsoft Connect.

The IE team is testing sites, libraries and other SVG content on the web. Our goal is to help authors with their content and find any bugs in our feature set. One important best practice is using feature detection, not browser detection when testing for SVG support. Help us find the places where developers are detecting specific browsers instead of testing for functionality, and make any changes to open source libraries, or contact content authors such that we can help fix any issues that may arise.

We’re excited to see web developers start using this technology and the next generation, graphically rich, websites built with SVG.

Patrick Dengler

Senior Program Manager

Internet Explorer


IEBlog