SVG Maker Project - Beating Hearts

This project shows two ways to animate a heart image without using any javascript. It will use another web standard called SMIL (Synchronous Multimedia Integration Language). The first method will switch between to images of the heart, one 100% in size and the other 90% in size. The second method will use a smoother transition and create a more pulsating heart.

First let's start with how to draw the heart. In the code shown below, the SVG element should look familiar, setting the width and height equal to 100% and the viewBox attribute telling the browser to use coordinates (0,0) and (280,280) as the coordinates of the container the image will be put into. Next, there are a couple of style definitions for the class attributes used in the image.

The <defs> element is used to place definitions. In this case we are going to define a symbol with id="Heart" - the id is case sensitive. The <symbol> has attributes id and viewBox. Since the symbol will be placed in a container SVG allows setting a viewBox attribute for the symbol, which uses coordinates (0,0) and (280,270) - which is 10 pixels less in height than the entire image. The <symbol> element contains 3 <path> elements, the last 2 of which draw the heart shape. It draws the heart in 2 halves. The first path element is just a vertical line and is included since some browser implementations will leave a line of whitespace between the 2 halves. The line just makes sure the whitespace is filled in. The next 2 path elements are the 2 halves of the heart and are mirror images, both start with a moveto command to move to (140,58) which is followed by two curveto (c) commands.

The curveto command draws a cubic Bézier curve. If Bézier curves are unfamiliar, reference the free eBook, Learn SVG Interactively. For a really good understanding, reference the free eBook STEAM Coded String Art. The curveto command has 2 control points and an ending point, so the c is followed by 3 sets of coordinates. The curveto command (c) is lower case, which means relative to the where the last command ended. (Upper case commands are absolute coordinates).

The first heart half is the right side of the heart. It is drawn by the second path element contained in the Heart symbol definition. The first curveto command has control points relative to (140,58), (30,-60) and (100,-60) and ends at (130,0). Add (140,58) to each coordinate to get its absolute position. The second curveto command is relative to the end point of the first curve and has control points (30,100) and (-110,150) and ends at (-130,210). If you copy the code and delete the third path element from the Heart symbol defintion, it will only display the right half of the heart.

The second heart half is the last path element in the Heart symbol definition. Notice in the curveto commands that the control and end points have the same y coordinates, but the x-coordinates are multiplied by -1.

The last part of the code has a group tag with a class defintion of heart, defining the styles for the grouping. The group contains the <use> element, which references the symbol with id="Heart" from the xlink:href attribute. The id referenced is preceded by a # sign indicating the id is defined elsewhere in the document.

Coding the Heart

<svg width="100%" height="100%" viewBox="0 0 280 280"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<style type="text/css"><![CDATA[
.heart {fill:red;stroke:black;stroke-width:0.5px;}
.vline {stroke:red;}
]]>
</style>

<defs>
<symbol id="Heart" viewBox="0 0 280 270">
<path d="M140,58L140,267" class="vline" />
<path d="M140,58c30,-60,110,-60,130,0c30,100,-110,150,-130,210" />
<path d="M140,58c-30,-60,-110,-60,-130,0c-30,100,110,150,130,210" />
</symbol>
</defs>

<g class="heart">
<use x="0" y="0" width="280" height="270" xlink:href="#Heart" />
</g>

</svg>

Animating the Heart - Method 1

To animate the Heart, the code highlighted in yellow below was added. 4 more styles were defined: show, hide, h1, and h2. A transform attribute was added to the grouping with class="heart", with both translate and scale transformations, which doesn't move or scale the grouping from its initial state. A second grouping with class="h1" was added and contains the previous <use> element, but also adds a <animate> element.

The <animate> element has attributes: attributeName, values, dur, and repeatCount. The attributeName is set to "class" indicating the class attribute of the parent element will be animated, which is the <g> element with class="h1". The values attribute tells the animation to switch between classes show and hide. The dur attribute sets the duration of the animation to 1 second and the repeatCount attribute tells the animation to repeat indefinitely.

The grouping with class="heart" is then duplicated (copied and pasted) and edited, changing the transformation attribute to translate(14,14) and scale(0.9), which will make the image 90% of its original size.

When the image is scaled, it needs to be positioned to keep it centered. 90% of 280 is equal to 252. 280-252=28. To center the image, half the pixel difference should be above the image and half below, so 28/2=14. Therefore the translate transformation is set to (14,14). Technically the y transformation should only translate 13.5 since the viewBox is 270, but it is close enough.

The second grouping is also edited to have class="h2" and the values attribute of the <animate> element is changed so the animation first sets the class atribute to hide, then to show.

The result is an animation that switches every half second between two Heart symbols, the first 100% of its size and the second 90% of its size.

<svg width="100%" height="100%" viewBox="0 0 280 280"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<style type="text/css"><![CDATA[
.heart {fill:red;stroke:black;stroke-width:0.5px;}
.vline {stroke:red;}

.show {display:initial;}
.hide {display:none;}

.h1 {display:initial;}
.h2 {display:none;}
]]>
</style>

<defs>
<symbol id="Heart" viewBox="0 0 280 270">
<path d="M140,58L140,267" class="vline" />
<path d="M140,58c30,-60,110,-60,130,0c30,100,-110,150,-130,210" />
<path d="M140,58c-30,-60,-110,-60,-130,0c-30,100,110,150,130,210" />
</symbol>
</defs>

<g transform="translate(0,0) scale(1)" class="heart">
<g class="h1">
<use x="0" y="0" width="280" height="270" xlink:href="#Heart" />
<animate attributeName="class" values="show;hide;" dur="1s" repeatCount="indefinite" />
</g>
</g>

<g transform="translate(14,14) scale(0.9)" class="heart">
<g class="h2">
<use x="0" y="0" width="280" height="270" xlink:href="#Heart" />
<animate attributeName="class" values="hide;show;" dur="1s" repeatCount="indefinite" />
</g>
</g>

</svg>

Animating the Heart - Method 2

To animate the Heart, the code highlighted in yellow below was added to the original Heart image above. A transform attribute was added to the grouping with class="heart", with a scale(1) transformation, which doesn't scale the grouping from its initial state. A second grouping with a transform attribute that has a translate(0,0) transformation and a class="h1" was was added. The grouping contains the previous <use> element, but also adds a <animateTransform> element.

The <animateTransform> element has attributes: attributeName, type, from, to, dur, and repeatCount. The attributeName is transform, its type is translate and it will animate the translate transformation from (0,0) to (14,14) in 0.8 seconds and repeat indefinetely.

Outside that grouping, but inside the outer grouping is another <animateTransform> element that was added. This animation will animate the scale transformation from 1 to 0.9 in 0.8 seconds and also repeat indefinetly.

The result is a smooth transformation as the image scales from 100% to 90% of its size and remains centered, but jumps back from 90% to 100% when repeating. This gives it the pulsing effect.

There can only be one <animateTransform> element per parent element, so the two groupings are needed to allow the two transformations to happen together.

<svg width="100%" height="100%" viewBox="0 0 280 280"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<style type="text/css"><![CDATA[
.heart {fill:red;stroke:black;stroke-width:0.5px;}
.vline {stroke:red;}
]]>
</style>

<defs>
<symbol id="Heart" viewBox="0 0 280 270">
<path d="M140,58L140,267" class="vline" />
<path d="M140,58c30,-60,110,-60,130,0c30,100,-110,150,-130,210" />
<path d="M140,58c-30,-60,-110,-60,-130,0c-30,100,110,150,130,210" />
</symbol>
</defs>

<g transform="scale(1)" class="heart">
<g transform="translate(0,0)" class="h1">
<use x="0" y="0" width="280" height="270" xlink:href="#Heart" />
<animateTransform attributeName="transform" type="translate" from="0 0" to="14 14" dur="0.8s" repeatCount="indefinite" />
</g>
<animateTransform attributeName="scale" type="translate" from="1" to="0.9" dur="0.8s" repeatCount="indefinite" />
</g>

</svg>

SVG Maker Projects - coding SVG images - at STEAMcoded.org

STEAMcoded.org STEAMcoded.org