Friday, July 3, 2015

Trying out Snap.svg

Snap.svg
On Wednesday I wrote about trying to use the jQuery SVG plugin to perform some simple manipulations to a SVG image directly embedded in an HTML document. While jQuery SVG is perhaps the most popular way to interact with SVG via JavaScript, I found it awkward to use. I decided to give another similar JavaScript package a try - Snap.svg.

Snap.svg is a JavaScript SVG library designed for modern web browsers and created by the author of the Raphaël library. Unlike Raphaël, Snap does not support browsers older than IE 9. This makes it smaller and more feature-rich - and let's face it, there are fewer and fewer reasons every day to support IE 8 and below.

To do a fair comparison between Snap and jQuery SVG, I performed the same task with both libraries. I have a really wide SVG scene with some frogs in it. The viewBox restricts the viewable part of the scene but changes when you click on one of the frogs.

Here is basically the same HTML/SVG I used on Wednesday, with most of the SVG cut out to save space.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Snap.svg Test</title>
</head>

<body>

    <div id="svgContainer">

        <svg id="svgScene" xmlns="http://www.w3.org/2000/svg"
                viewbox="0 0 500 282">
            ....
            <g id="ceo-frog">
                ....
            </g>
            <g id="science-frog">
                ....
            </g>
            ....
        </svg>

    </div>

</body>

<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="snap.svg.js"></script>

</html>

The difference from Wednesday's jQuery SVG page is obviously the script at the bottom. I am including the snap.svg.js file (there's a minified version available too) but unlike the jQuery plugin, Snap does not need any CSS. I therefore don't have any stylesheets included in this one. I am still using jQuery though, but just for the convenience of the function that can be called when the DOM is ready. Snap does not itself rely on jQuery.

Here is the JavaScript that sets up and performs the animation.

$(function() {

    var s = Snap("#svgScene");

    var vb = s.attr("viewBox");
    var bounds = s.getBBox();

    // note that the viewBox object is not the same
    // as the bounds object!

    var ceo = Snap("#ceo-frog");
    var sci = Snap("#science-frog");

    ceo.click(function() {
        s.animate({viewBox: "500 0 500 282"}, 1500, mina.easeinout);
    });

    sci.click(function() {
        s.animate({viewBox: "0 0 500 282"}, 1500, mina.easeinout);
    });

});

On line 3, I get a Snap object that represents the SVG root element. The Snap() function takes as a parameter a selector much like jQuery does for finding the SVG elements on the page.

Lines 5 and 6 (and the comments on 8 and 9) have no bearing on the rest of the code, but I wanted to include them just for informational purposes. The Snap.attr() function is a way to get attribute values for SVG elements. But when calling s.attr("viewBox"), Snap is smart enough to return a special object representing the viewBox, not just its String value. Nice. This is different from calling s.getBBox() however. The .getBBox() function returns an element's bounding box. In the case of my SVG scene that means an object representing the entire width, not just the currently visible portion. Just wanted to point that out. I was impressed.

Continuing on, lines 11 and 12 get the frog objects from the SVG. Notice how you can call Snap() directly to get these - you don't need to pass a reference to the root SVG node or anything. This is quite a departure from jQuery SVG. Once I have those objects, setting up click event handlers follows a pattern much like jQuery event handlers. Lines 14 and 18 are examples of that.

Animation of the viewBox change is also nicer than with jQuery SVG, in my opinion. Snap SVG Element objects (above, s, ceo, and sci are all Elements) have an animate function which is quite similar to jQuery's. To animate a property of the element, you just specify it in an object along with its target value. In my example I am animating the viewBox attribute by specifying the String value I want it to be when the animation finishes. This is done on lines 15 and 19. The second parameter to the animate function is the length of time in milliseconds that the animation should last. Again, this is just like jQuery. The third parameter is optional but specifies the animation's easing function. By default, a jQuery animation easing looks more like the Snap easeinout easing so I specified that to make them match more closely.

Here's what it looks like in action:



What's my conclusion after comparing these two SVG libraries? While the number of lines of code needed to perform the simple click-to-animate-the-viewbox action was basically the same in both versions, I think the Snap API makes more sense. Snap's documentation is also much better than jQuery SVG's. Even so, I wouldn't mind seeing a few more demos linked directly from the documentation (there are some cool demos, but no easy way to view the code behind them), and that viewBox object behavior doesn't even appear to be documented (at least I couldn't find it). After using both to perform the same task, I have to say that I prefer Snap and think I will use it going forward. I would also recommend it if you want to manipulate SVG on a web page. It has a ton more features that I've shown here - this example is extremely basic! Give it a try for yourself.

And enjoy the holiday weekend, if you live in the United States. Enjoy the regular weekend if you live anywhere else in the world.

Amphibian.com comic for 3 July 2015