Although SVG has been around for more than a decade, it became popular in the last few years as a way to draw charts in web applications, thanks to some great libraries that have made beautiful charts and drawings effortlessly available to developers: in particular D3.js for charts and Raphaël for cool SVG drawings and animations.
New outstanding libraries have been recently emerging; they provide front-end developers and designers with new approaches and amazing new features:
- Snap.svg, as we are going to see, offers the newest SVG features like masking, clipping, patterns, gradients, etc…
- PathsJs is a minimal library for SVG-based charts creation. It’s designed to support reactive programming by generating SVG paths that can be used with template engines. It works best with a mustache-based template engine, such as Ractive.
- Although it is not SVG-based, P5 deserves a mention. It is an attempt, and apparently a good one, to overcome traditional issues affecting the HTML5 canvas element – interaction in particular.
In the rest of this article, we are going to take a good look at Snap.svg, starting from the basics.
Raphaël
If you haven’t had a chance to take a look at Raphaël, you probably should. It’s a nice piece of JavaScript created as a solo project by Dmitry Baranovskiy. Although it started as a personal project, the result is remarkable for interface (very clear and consistent), performance, and appearance (especially for animations). The library is more oriented toward “freehand” drawing and animations rather than charts. The gRaphaël extension was later released to address this, but it hasn’t become as popular and widespread as D3.
Despite being ahead of other libraries, in time Raphaël started showing its limits. For example, in order to be compatible with older browsers Raphaël doesn’t support all those cool new SVG features that would make your animations stand out.
That’s why its author decided to start fresh with a new project, Snap.svg, which of course benefits from the experience gathered designing Raphaël. Snap.svg also breaks with the past, allowing the introduction of a whole new kind of special effects.
Oh, Snap!
Before delving into Snap’s syntax and getting started with a few examples, let’s quickly review the pros and cons of this new library:
Pros:
- It supports all the cool features we mentioned above.
- Snap can wrap around and animate existing SVG. You could generate your SVG with tools like Adobe Illustrator, Inkscape, or Sketch, or load strings of SVG asynchronously and query out the pieces you need to turn an SVG file into a sprite.
- It is free and open source.
Cons:
- It’s a low-level library, so if you need to visualize data, unfortunately there is no support for charts yet.
- There is no support for data-binding.
- Snap is a young project that has yet to reach full maturity. It is already great to use for your personal projects, but you have to weight this aspect before using it in a complex one.
As we mentioned, Snap uses features not supported by older browsers. Although a complete, updated compatibility table is not given yet, this library should work fine at least with the following browser versions (and newer):
- Firefox ESR 18
- IE 9.0.8
- Chrome 29
- Opera 24
Getting Started with Snap
After downloading the source files from the GitHub repository, you can unzip them and look for the dist
folder, which contains the built distribution files. For detailed instructions about building snap with Grunt, or for checking for the latest version, take a look here.
Once you’ve copied the minified version of the file inside the js
folder of your new project, just include the script in your HTML page. Assuming it’s located in the root directory of your project, you can just add this line right before the page’s closing body
tag:
<script src="/js/snap.svg-min.js"></script>
Now we are ready to create a drawing area for our vector graphic. We have two ways to do this:
- Create a brand new drawing surface, that will be appended to the page’s DOM (inside
body
). - Re-use an existing DOM element, and wrap it in a Snap structure. You can wrap any element, but for drawing methods you’ll need an SVG element.
The first way allows you to explicitly set width and height of the surface area at creation in the JavaScript code. If you’d like to achieve a greater level of separation between presentation and content, you can use the second way, specifying the values in a CSS rule. At a high level, the first method allows you to adjust the drawing surface’s appearance dynamically, but if you don’t need to, the second way is more MVC-compliant. Moreover, wrapping is what allows you to import and modify SVG drawings created with external tools, as mentioned in the introduction section.
So, for example, to create a new 800-by-600 pixels drawing area, you just need the following line of JavaScript:
var s = Snap(800, 600);
If, instead, you want to wrap an existing one, say #complexSVGfromIllustrator
:
<svg id='complexSVGfromIllustrator' version="1.1" xmlns="https://www.w3.org/2000/svg">
...
</svg>
You can still get away with a single line of JavaScript, to import the drawing surface:
var s = Snap('#complexSVGfromIllustrator');
Side note: for the curious reader: if you inspect the Snap objects after creation, you’ll notice they have a paper
field, testifying to Raphaël’s legacy.
Shapes
Once we have created our drawing surface, our Snap
wrapper, it is time to draw some shapes on it. Let’s say you’d like to draw a circle:
var paper = Snap('#complexSVGfromIllustrator'),
circle = paper.circle(100, 50, 10);
As you can see from the docs, the first two parameters in the circle()
method are the coordinates of its center, while the third one is the circle’s radius. All these parameters are mandatory, and failing to provide them will result in an error being thrown. The circle()
method, as with all the other drawing methods, will return a reference to an object.
You can draw ellipses too, as shown in the following code sample. Vertical and horizontal radii are needed this time. Again, all parameters are mandatory.
var ellipse = paper.ellipse(100, 50, 10, 20);
If you’d like to draw a rectangle, use the following code. This will create a rectangle with its top-left corner at (100px, 100px), a width of 200px, and a height of 200px.
var r = paper.rect(100, 100, 200, 300);
The cool thing about the rect()
method, is that it also accepts two optional parameters that control the radius of rounded corners, independently for vertical and horizontal axes. These parameters default to 0 when not passed, but be careful that if you only pass one (the horizontal radius), the second one will not be set to zero, but instead both will assume the same value.
var rect = paper.rect(100, 100, 200, 300, 10); //equivalent to paper.rect(100, 100, 200, 300, 10, 10);
Now, if you wanted to start from scratch, you could create another drawing surface, or you could just use the paper.clear()
method to erase all drawings from paper
.
Lines and Polygons
To cover more complicated drawings, we need to take a step back, and talk about drawing lines. As you would expect the method takes the four coordinates of a line’s endpoints, as shown below.
var line = paper.line(10, 100, 110, 200);
What’s far more interesting is the possibility to draw complex polylines: var line = paper.polyline(10, 100, 110, 200);
is in principle equivalent to the line()
method above, but you’d probably be surprised by its visual outcome. To see why, let’s try this
var p1 = paper.polyline(10, 10, 10, 100, 210, 20, 101, 120);
paper.polyline()
and paper.polygon()
are aliases for the same method, and by default the resulting (closed) polygon is drawn with black fill and no stroke. That’s why you could not see the line drawn with polyline()
above (although you can check, by inspecting the page, that the SVG code for it has been indeed appended to its container).
To change this behavior, as well as the appearance of other elements, we must introduce attributes.
Attributes
The notion of attributes for Snap elements is somewhat broader than usual, meaning that it includes both HTML attributes and CSS attributes under the same interface (while most other libraries makes a distinction between .attr()
method for HTML attributes and ‘.style()’ for CSS ones). By using the element.attr()
method on a Snap wrapper object, you can set its class
or id
, as well as its color or width.
As mentioned above, using Snap you have two ways to assign CSS properties to an element. One is to include these properties in a separate CSS file, and then just assign the proper class to your element:
.big-circle {
stroke: red;
stroke-width: 2;
fill: yellow;
}
circle.attr({class: 'big-circle'});
The same result can be obtained by assigning these properties using JavaScript:
circle.attr({
stroke: 'red';
stroke-width: 2;
fill: 'yellow';
});
Once again, the first way allows a better separation between content and presentation, while the second one provides the possibility to dynamically change attributes. If you are thinking about mixing the two strategies, keep in mind that the rules defined in a CSS file will trump the one you assign with element.attr()
, despite the temporal order in which they are assigned to elements.
If you haven’t maintained a reference to the element you want to style, don’t worry, you can easily grab it using CSS selectors:
circle = paper.select('circle'); //First circle in paper's DOM tree
circle = paper.select('circle.big-circle'); //First circle in paper's DOM tree which has class 'big-circle'
circle = paper.select('circle:nth-child(3)'); //Third circle in paper's DOM tree
circle = paper.selectAll('circle.big-circle'); //All circles in paper's DOM tree with class 'big-circle'
Groups
SVG elements can be grouped so that common transformations and event handling can be more easily applied to all the elements in a group. Creating a group is easy:
var group = paper.g(circle, rect);
var g2 = paper.group(rect, circle, ellipse); //an alias for paper.g
Be careful: Order or the arguments matters! Second, if you assign an element to a group, it will be removed from any group it might already belong to.
Elements can, of course, also added to existing groups after they are created:
group.add(circle);
Images
Snap supports nesting raster images inside SVG elements, loading it asynchronously and displaying it only on load completion.
var img = paper.image('bigImage.jpg', x, y, width, height);
The resulting object can be treated as an SVG element. Note, if you use select()
on images to retrieve them later, the wrapper created will be the one for HTML elements, so most of the methods available for SVG elements won’t be supported.
Transformations
We have seen how to draw asymmetric polygons like ellipses and rectangles. However, basic methods constrain us to draw these figures aligned to the Cartesian axes. What if we wanted to draw an ellipse whose axes are 45° rotated with respect to the x-y axes? We can’t specify this in the creation methods, but we can use transformations to obtain the same result.
Likewise, we might need to rotate an image, or to move an element (or a group) at some point after its creation. The transform()
method allows us to do so, by passing an SVG transformation string:
var ellipse = paper.ellipse(100, 50, 10, 20);
ellipse.transform('r45');
This method can take either a string or an object as input. We can also use the transformation matrix associated with an element to apply the same transformation to another element:
var e1 = paper.ellipse(100, 50, 10, 20),
e2 = paper.ellipse(200, 50, 12, 24);
e1.transform('r45');
e2.transform(e1.matrix);
Be careful: the center of transformation for the second element will still be the one used for the first one, so the final effect might surprise you.
The transform()
method can also be used to retrieve the transformation descriptor object for the element it is called on – just call it without arguments. This descriptor can be used to retrieve the local transformation matrix and difference matrix in the case of nested elements:
var g1 = paper.group(),
e1 = paper.ellipse(200, 50, 12, 24);
g1.add(e1);
g1.transform('r30');
e1.transform('t64.6447,-56.066r45,0,0');
console.log(e1.transform());
Conclusion
This article provided an introduction to the basics of Snap.svg. If you are interested in seeing the coolest stuff, please stay tuned, as an advanced follow-up will be published soon.
If you want to learn more about data visualization and Snap, here are a few useful resources:
- Snap.svg tutorial.
- Slides from a presentation about data-visualization and advanced techniques.
- Take a look at some of the code above in action on CodePen.
Frequently Asked Questions (FAQs) about Snap SVG
What is the difference between Snap SVG and other SVG libraries?
Snap SVG is a powerful and flexible library designed specifically for working with Scalable Vector Graphics (SVG). Unlike other SVG libraries, Snap SVG provides a simple and intuitive API for animating and manipulating both existing SVG content and SVG content generated with Snap SVG. It also supports most SVG features, including gradients, patterns, and clipping paths, and it’s compatible with all modern browsers.
How can I start using Snap SVG?
To start using Snap SVG, you need to include the Snap SVG JavaScript file in your HTML document. You can download it from the official Snap SVG website or include it directly from a CDN. Once you’ve included the Snap SVG file, you can start creating and manipulating SVG content using the Snap SVG API.
Can I use Snap SVG for complex animations?
Yes, Snap SVG is perfect for creating complex animations. It provides a powerful animation API that allows you to animate any SVG attribute. You can also use Snap SVG’s matrix transformations to create complex animations with ease. Plus, Snap SVG supports easing functions, which can be used to create smooth and natural animations.
Is Snap SVG compatible with all browsers?
Snap SVG is compatible with all modern browsers, including Chrome, Firefox, Safari, Opera, and Internet Explorer 9 and above. However, some SVG features may not be fully supported in older browsers.
How can I create interactive SVG content with Snap SVG?
Snap SVG provides a simple and intuitive API for creating interactive SVG content. You can use Snap SVG’s event handling functions to respond to user interactions, such as clicks or mouse movements. You can also use Snap SVG’s animation API to create interactive animations.
Can I use Snap SVG with other JavaScript libraries?
Yes, Snap SVG can be used alongside other JavaScript libraries. It doesn’t interfere with other libraries, and it doesn’t modify any native JavaScript objects. This makes Snap SVG a great choice for projects that use other JavaScript libraries.
How can I manipulate existing SVG content with Snap SVG?
Snap SVG provides a powerful API for manipulating existing SVG content. You can use Snap SVG’s functions to change the attributes of SVG elements, transform SVG elements, and animate SVG elements. You can also use Snap SVG’s selector functions to select specific SVG elements.
Can I use Snap SVG to create SVG filters?
Yes, Snap SVG supports SVG filters. You can use Snap SVG’s filter functions to create and apply SVG filters to SVG elements. This allows you to create a wide range of visual effects, such as blurring, lighting, and color adjustments.
How can I create gradients with Snap SVG?
Snap SVG provides a simple API for creating SVG gradients. You can use Snap SVG’s gradient functions to create linear and radial gradients, and you can use Snap SVG’s color functions to define the colors of the gradient.
Can I use Snap SVG to create SVG patterns?
Yes, Snap SVG supports SVG patterns. You can use Snap SVG’s pattern functions to create and apply SVG patterns to SVG elements. This allows you to create a wide range of visual effects, such as textures and patterns.
I'm a full stack engineer with a passion for Algorithms and Machine Learning, and a soft spot for Python and JavaScript. I love coding as much as learning, and I enjoy trying new languages and patterns.