Creating an Animated Valentine’s Day Card with Snap.svg

Ivaylo Gerchev
Share

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!