Creating an Animated Valentine’s Day Card with Snap.svg
Last year I showed you how to create a resolution independant infographic with Snap.svg library. Snap is a JavaScript library designed to make it easy to work with SVG.
While we obtained a good result, our creation was mostly a static drawing without any real bells and whistles.
Today I'm going to show you how to achieve some cool effects and animations while we create an animated SVG Valentine's Day love card. You'll also learn how to incorporate Google's web fonts as well your SVG drawings and some advanced text manipulations.
While it’s relatively simple to create static SVG in a host of graphics programs, Snap.svg is one of the best ways to create dynamic, interactive SVGs. We will cover some techniques that you should be able to apply to many different applications.
Let's get started.
First, open up your favorite editor and create a new HTML document. Then we add references for two of the Google's web fonts and for the Snap.svg library.
We'll do this by putting the next 3 lines inside the head
tag. This sets up Snap for us and includes a couple of Google Fonts.
<link href='http://fonts.googleapis.com/css?family=Niconne' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Oleo+Script' rel='stylesheet' type='text/css'>
<script src="http://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js">
</script>
Now, inside the body
tag, we create a script
tag and put the following code in it:
window.onload = function () {
var card = Snap(600, 400);
}
The windows.onload
function makes sure that the page is fully loaded before executing any JavaScript. The card
variable represents our SVG canvas with width
and height
set to 600 and 400 pixels.
<!-- SVG Heart Shape -->
<svg version="1.1" id="heart" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" x="0px" y="0px"
width="300px" height="200px" viewBox="0 0 300 200" enable-background="new 0 0 300 200" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" d="M149.95,43.749C111.115-25.583,0.729-9.406,0.002,71.063
c-0.402,44.195,52.563,60.716,87.829,78.384c34.196,17.135,58.534,40.574,62.347,50.553c3.266-9.777,30.373-33.88,62.028-51.032
c34.612-18.755,88.231-34.189,87.829-78.385C299.304-10.087,186.998-22.818,149.95,43.749z"/>
</svg>
Next, we put the above raw SVG code below the script
tag. This is a SVG path for the heart shape which we'll use a bit later. I used Inkscape to create the path data. Then, we move back inside the script
tag and place the following lines of code below the card
variable.
// Utilities
var dropShadow = card.paper.filter(Snap.filter.shadow(0, 2, 3))
var bgGradient = card.paper.gradient("r(0.5, 0.5, 0.5)#EE2C34-#821D2D");
var barGradient = card.paper.gradient("l(0, 0.5, 1, 0.5)#00ADEF-#EC008B:75");
These are some utilities: a drop shadow filter and two gradients (one radial and one linear). We'll need them throughout the code below.
Now, we are ready to start building the actual card. We begin by adding a warm red sunburst background.
// Card's Background
var background = card.paper.rect(0, 0, 600, 400).attr({fill: bgGradient, stroke: "none"})
var rays = card.circle(300, 200, 300).attr({
fill: "none",
stroke: "red",
strokeWidth: 580,
strokeDasharray: "20 20",
opacity: 0.2
});
We create a rectangle and fill it with our already defined radial gradient. The next thing we want to make is a bunch of rays spreading out from the center of the card. This seems tricky, but thanks to a small stroke trick we'll achieve that effect pretty easily.
First, we create a circle with red stroke and fill set to "none", and then, to mimic the effect of rays, we set strokeWidth
to be extremely tick (filling the whole canvas) and give the strokeDasharray
attribute a value of "20 20" (resulting in a dashed line).
OK. The stage is now set! Let's add the actors.
// Hearts Shapes
Snap.select("#heart").appendTo(card);
var heartLeft = Snap.select("path").attr({
fill: "#00ADEF",
filter: dropShadow,
transform: "t50,80",
opacity: 0.9
}).insertAfter(rays);
var heartRight = heartLeft.clone().attr({
fill: "#EC008B",
filter: dropShadow,
transform: "t250,80",
opacity: 0.9
});
Now, we are need the SVG code which we place at the end of our body
tag. First, we select it by its ID #heart
and append it to the canvas. Then, we select the actual path, give it the desired attributes, and make sure that it is inserted after the rays (which means above them). Next, we create the second heart shape by cloning the first one and changing the fill
and transform
attributes.
We have two lovely hearts and now we want to position the text "I LOVE U" on them. Here we'll call on the Google's web font "Oleo Script".
// Hearts Texts
var iuText = card.paper.text(185,200, ['I','U']).attr({
fill: "#B5DD25",
fontFamily: "Oleo Script",
fontSize: "72px",
filter: dropShadow,
opacity: 0
}).animate({opacity: 1}, 2000);
iuText.select("tspan:nth-child(2)").attr({dx: "160px"});
var loveText = card.paper.text(290,85, ["L", "O", "V", "E"]).attr({
fill: "#B5DD25",
fontFamily: "Oleo Script",
fontSize: "24px",
filter: dropShadow,
opacity: 0
}).animate({opacity: 1}, 2000);
loveText.selectAll("tspan").attr({x: 290, dy: "1.2em"});
The first chunk of text creates two strings "I" and "U". By default they are positioned them together, but we want them separated. We do this by selecting the "U" string and moving it away 160 pixels (by using the dx
attribute).
The second text creates four strings: one for each letter from the word "love". The last line of code make the text to appear vertically by selecting each letter and giving it one and the same coordinates for x
, and setting the dy
attribute to "1.2em".
Now, it's time to move to the bottom of our card.
var ribbon = card.paper.rect(0, 300, 600, 70).attr({fill: "#6F2570", filter: dropShadow});
var bar = card.paper.rect(0, 300, 600, 10).attr({fill: barGradient});
Here, we create a ribbon with a bar at the top. We apply the drop shadow effect to the ribbon and fill the bar with a linear gradient with colors matching those from the heart shapes. And the next thing we're going to add is a cool rotating text above the ribbon. This time we'll use the Google's web font "Niconne".
// Rotating Wish Text
setTimeout(function(){
var wishText = "Happy Valentine's Day \u2763"; // create the wish text
var charsGroup = card.g(); // create a group for the individual characters
var charsArray = wishText.split(''); // split the wish text to characters
var wishTextChars = charsGroup.text(110, 355, charsArray).attr({ // create a text element with an individual string for each character
fill: "#D6DF23",
fontFamily: "Niconne",
fontSize: "42px",
filter: dropShadow,
opacity : 1,
});
var singleChars = charsGroup.selectAll("tspan").attr({opacity: 0}); // select all strings/characters and hide them initially
function charsAnimation( element ) { // a function for animating each character
Snap.animate(0,1, function( value ) {
element.attr({ opacity: value, rotate: (value * 360) });
}, 2000 );
}
for( var i=0; i < singleChars.length; i++ ) { // go through each character and animate it
setTimeout(
charsAnimation.bind(null, singleChars[i]) , i * 300);
}
}, 2000);
To complete our creation we're going to add a randomized raining hearts effect. This is something you’d find hard to produce in a standard SVG graphics program.
We’re going to randomize four characteristics of this rain. Two different heart characters will be rendered at randomized sizes, colors and timings.
// Hearts Rain Effect
setTimeout(function(){
function randomNums(min, max) { // a function generating random numbers
return Math.floor( Math.random() * ( 1 + max - min ) ) + min;
}
setInterval(function () { // create new hearts set every 2 seconds
for (var i = 1; i < 29; i++) {
var hearts = ["\u2764", "\u2665"]; // unicode characters for hearts
var colors = ["#D691BF","#D74498", "#A54A9C"]; // different colors for the hearts
var heart = randomNums(0,1);
var color = randomNums(0, 2);
var size = randomNums(3, 6);
var time = randomNums(5, 10);
var mutableX = i * 20;
var heartsRain = card.paper.text(mutableX, -50, hearts[heart]).attr({
fill: colors[color],
fontSize: size * 5,
opacity: 0.3
}).animate({transform: "t0,500", opacity: 0.9}, time * 1500, mina.linear);
};
}, 2000);
}, 7000);
And now we're done. You can see the final outcome here:
See the Pen vEpQOr by (@Secret Sam ) on CodePen.
As you can see by combining our imagination with the capabilities of Snap.svg we can create many cool drawings and animations like the beautiful animated love card we've just created.
I hope you've enjoyed the tutorial and I wish Happy Valentine's Day to all of you!