Sei sulla pagina 1di 48

How to Use

“animateTransform” for
Inline SVG Animation
by Kezz Bracey21 Oct 2014
Difficulty:BeginnerLength:MediumLanguages:
SVG AnimationSketchUI Design
Today we’ll be stepping you through the basics of using  animateTransform  to
generate inline animations with SVG (scalable vector graphics).

If you’re brand new to SVG I recommend checking out Getting Started With Scalable
Vector Graphics (SVG) to bring you up to speed.

The techniques you’ll be learning will allow you to create sophisticated icon and
image animations without a single GIF, JPEG or PNG, with zero JavaScript, and
without the faintest whisper of Flash.

The animations you create will be easy to edit later because they’re pure code, and the
results will only take up a couple of KB of precious bandwidth when they’re viewed.

Before We Begin
To animate SVG shapes you’ll first need the ability to create them. I’ve found the
easiest way to create SVGs is to use Sketch from Bohemian Coding. If you don’t own
Sketch you can grab a free 30-day trial for the purposes of this tutorial.

We’ll be manipulating the SVG code, so after you’ve drawn a shape in Sketch, create
a slice around it and export that slice as an SVG file.
You’ll then be able to open your exported file in a code editor (like Sublime Text) and
copy the SVG code from within. All you need is the code from the
opening  <svg>  tag to the closing  </svg>  tag.

For example, Sketch generates the following SVG code for the blue rectangle pictured
above:
1<svg width="100px" height="125px" viewBox="0 0 100 125" version="1.1" xmlns="http://www.w3.o
2xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
3    <!-- Generator: Sketch 3.1 (8751) - http://www.bohemiancoding.com/sketch -->
4    <title>Slice 2</title>
    <desc>Created with Sketch.</desc>
5    <defs></defs>
6    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type
7        <rect id="Rectangle-1" fill="#3C81C1" sketch:type="MSShapeGroup" x="0" y="0" width="1
8    </g>
9</svg>
To make the code visually easier to work with, we’ll make a couple of little changes
to the code. 

Set the  svg  element’s  width  and  height  to  100%  and delete the  viewBox  setting.
Also delete the  Generator comment, and the  title ,  desc ,  defs  and  g  elements. 
You should end up with something like this:
1 <svg version="1.1" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xl
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
2   <rect id="Rectangle-1" fill="#3C81C1" sketch:type="MSShapeGroup" x="0" y="0" width="100"
3 </svg>
Drop that code into an HTML document and, when viewed in the browser, you should
see the same blue rectangle on your page as you saw in Sketch:

Note: The above image includes an X and Y axis in the background as you'll need to
understand these in order to create your animations. You’ll learn why shortly.

What Does “animateTransform” Do?


The short answer is, the  animateTransform  element generates animations by setting
transform properties on the SVG shape to which it's applied.

Given that, the logical next question is:

Okay, So What Does “Transform” Do?


Adding  transform  settings to a shape allows you to modify how that shape appears
in 2D space. There are five different types of transformation you can perform:
1. translate

2. scale

3. rotate

4. skewX  
5. skewY

The  animateTransform  attribute allows you to animate between varying states of


transformation, so before you start animating it’s important to understand how
transformations actually work.

Transformations and 2D Space


Because transformations operate in 2D space their settings all relate to X and Y
co-ordinates plotted on an X / Y axis, as you saw pictured behind our blue
rectangle earlier.

 The  X  axis is the horizontal line in 2D space, and the  Y  axis is the vertical
line. By default, every shape starts at a position of  0  on both
the  X  and  Y  axis.
 From the  0  position on the  X  axis, positive values correspond with moving to
the right, and negative values correspond with moving to the left.
 From the  0  position on the  Y  axis, positive values correspond with moving
downwards, and negative values correspond with moving upwards.

If this doesn't make complete sense yet don’t worry, as it will become much clearer as
you see the examples of each type of transformation below. 

Don’t worry about the code for these transformations either, as we’ll cover that when
we move onto creating animations. To start with I just want you to get the essentials
down on what the five types of transformation actually do.
Translate
This shifts the shape’s position on the X axis (horizontal) and Y axis (vertical).

For example, here is our blue rectangle with  translate  values of  150  on
the  X  (horizontal) axis and  20  on the  Y  (vertical) axis:

Remember from the section above that positive values on the  X  axis correspond with
moving to the right, and positive values on the  Y  axis correspond with moving
downwards.

By setting the  translate  value for  X  to positive  150 , our rectangle has moved to
the right by 150 pixels. Setting the value for  Y  to positive  20  has moved our
rectangle down by 20 pixels.

Scale
This multiplies the shape’s overall size on the X axis (width) and Y axis (height).

Scale settings work as multipliers of the shape’s original size. For example, if we set
the  X  scale to  3  it would make the shape three times wider. If we set the  Y  scale
to  1.25  it would make the shape one and a quarter times higher, like so:
Al
so translated (150, 20)

Rotate
This rotates the shape around a given point by degrees.

Rotation works by setting the number of degrees by which you want to rotate the
shape. For example, here is our rectangle rotated by 45 degrees:
Al
so translated (150, 20)

By default the shape will rotate around its top left corner but you can also have it
rotate around a different point. We’ll cover how that’s done later in the tutorial.

SkewX
This skews the shape along the X (horizontal) axis.

Skewing along the X axis also works in degrees. For example, in the image below our
rectangle is skewed by 20 degrees along the X axis:
Al
so translated (150, 20)

SkewY
This skews the shape along the Y (vertical) axis by degrees.

SkewY  works in exactly the same way as  SkewX , only the transformation happens
vertically along the Y axis like so:

Al
so translated (150, 20)
Animating Transformations
Now that you know what transforms actually do, you can start creating animations
between different states of transformation. The basic process has three steps:

1. Set an initial state of transformation: the  from  state.


2. Set a second state of transformation: the  to  state.
3. Set the timing and repetition for an animated transition between the  from  and
the  to  state.

This is best understood through a practical example, so let’s start by animating


a  translate transformation.

Animating Translate Transformations


You’ll recall that earlier we began with our blue rectangle at its default position
of  0 0 , i.e.  0  on the  X  axis and  0  on the  Y  axis. We’ll set this position as
our  from  state.

We then looked at an example of that same blue rectangle with translate settings
of  150 20  applied, i.e.  150  on the  X  axis and  20  on the  Y  axis. We’ll set this
position as our  to  state.

Using  animateTransform  we can make the rectangle slide smoothly between
our  from  and our  to  states over a period of two seconds.

Your SVG shape, in this case rectangle, will need to have both opening and closing
tags e.g.  <rect></rect> . 

The  animateTransform  attribute should be placed in between these tags like so:
01 <rect id="Rectangle-1" fill="#3C81C1" sketch:type="MSShapeGroup" x="0" y="0" width="100"
  <animateTransform attributeName="transform"
02
    type="translate"
03     from="0 0"
04     to="150 20"
05     begin="0s"
06
07     dur="2s"
    repeatCount="indefinite"
08   />
09 </rect>
10
Take a look at the properties that have been set within the  animateTransform  tag.
These are what control how your animation runs. 

We have set  type  to translate, meaning we’ll be applying


a  translate  transformation type. As planned, we’ve set our  0 0  position in
the  from  state, and our  150 20  position in the  to  state. 

The value for  begin  is set to  0s , meaning the animation will begin zero seconds
after load, and  dur  is set to  2s , meaning the animation will run over a period of two
seconds. Finally, we’ve included  repeatCount  set to  indefinite  meaning the
animation will replay on loop.

This gives us the following animation:

Animating Other Transformations


The process is exactly the same for rotating all four of the other types of
transformations. After setting the  type  value to whatever type of transformation you
want, enter values for your  from  and  to  transformation states.

For example, we can use the following  animateTransform  settings to scale our
rectangle up to the size you saw in the earlier section on scale transformation:
1
<animateTransform attributeName="transform"
2   type="scale"
3   from="1 1"
4   to="3 1.25"
5   begin="0s"
  dur="2s"
6
  repeatCount="0"
7 />
8
Because scale transformation settings multiply the shape’s original size we start with a
value of  1 1  on the  from  setting. Doing this sets its original size at a multiplication
of 1.

Our  to  setting of  3 1.25  will animate our scale transformation up to three times the
original width on the X axis, and one and a quarter the original height on the Y axis.

Note: You’ll find your actual in-browser animations run much smoother than the
screen capture GIF you see above.
Animate Multiple Transformations
We can also combine the two animations we’ve created so far, to both translate and
scale at the same time. You can only use a single  animateTransform  tag inside
your  rect  tag, so to use multiple animations you'll need to incorporate a set
of  g  tags, which represent a group of SVG objects.

To make this work, add opening and closing  <g></g>  tags around your
existing  rect  tags:
01
02 <g>
03 <rect id="Rectangle-1" fill="#3C81C1" sketch:type="MSShapeGroup" x="0" y="0" width="100"
  <animateTransform attributeName="transform"
04     type="scale"
05     from="1 1"
06     to="3 1.25"
07     begin="0s"
    dur="2s"
08     repeatCount="0"
09   />
10 </rect>
11 </g>
12
Then add your second animation outside the closing  </rect>  tag, but before the
closing  </g>  tag. In this case we’re going to reintroduce our translate transformation:
01
02 <g>
03 <rect id="Rectangle-1" fill="#3C81C1" sketch:type="MSShapeGroup" x="0" y="0" width="100"
  <animateTransform attributeName="transform"
04     type="scale"
05     from="1 1"
06     to="3 1.25"
07     begin="0s"
08     dur="2s"
    repeatCount="0"
09   />
10 </rect>
11 <animateTransform attributeName="transform" type="translate" from="0 0" to="150 20" begi
12 </g>
13
We now have both scale and translate transformations animating simultaneously:
You can use  animateTransform  once per shape or group. If you need to add more
animations, wrap another set of group tags around your code and nest your
additional  animateTransform  tag inside it.

Let’s take a quick look at the remaining three transformation types, each one created
by changing only the  type ,  from  and  to  settings, and also combined with our
original translation animation.

Rotation Animation (plus Translation)

In this example  type  has been set to  rotate ,  from  has been set to  0  to begin with
no rotation, and  to has been set to  45  so we rotate 45 degrees over two seconds:
SkewX and SkewY Animation (plus Translation)

In the following two animations  type  has been set


to  skewX  and  skewY  respectively,  from  is set to  0  to begin with no skewing,
and  to  has been set to  20  so we skew by 20 degrees over two seconds:

An
imated skewX transformation
An
imated skewY transformation

Practical Example: Loading Icon


There are an infinite number of possibilities for animations you can create using these
basic techniques, but one of the most immediately useful is the creation of loading
icons where you might formerly have relied on animated GIFs.

This is a loading icon I created in about five minutes using Sketch:


I then exported it to an SVG file which gave me the following code:
0 <svg width="36px" height="36px" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
1     <!-- Generator: Sketch 3.1 (8751) - http://www.bohemiancoding.com/sketch -->
0     <title>loader01 2</title>
2     <desc>Created with Sketch.</desc>
0     <defs></defs>
3     <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:ty
        <g id="Group-3" sketch:type="MSLayerGroup" fill="#4990E2">
0             <rect id="Rectangle-1" sketch:type="MSShapeGroup" x="16.5873418" y="0" width="3
4             <rect id="Rectangle-2" fill-opacity="0.58" sketch:type="MSShapeGroup" x="16.67
0             <rect id="Rectangle-4" fill-opacity="0.79" sketch:type="MSShapeGroup" transfor
5 x="30.0303797" y="13.3857868" width="3" height="9.13705584"></rect>
0             <rect id="Rectangle-3" fill-opacity="0.37" sketch:type="MSShapeGroup" transfor
x="3.23544304" y="13.4771574" width="3" height="9.13705584"></rect>
6             <rect id="Rectangle-4" fill-opacity="0.72" sketch:type="MSShapeGroup" transfor
0 x="28.2582441" y="20.1076435" width="3" height="9.13705584"></rect>
7             <rect id="Rectangle-3" fill-opacity="0.3" sketch:type="MSShapeGroup" transform
0 x="5.00757864" y="6.75530065" width="3" height="9.13705584"></rect>
            <rect id="Rectangle-4" fill-opacity="0.65" sketch:type="MSShapeGroup" transfor
8 x="23.37111" y="25.0406255" width="3" height="9.13705584"></rect>
0             <rect id="Rectangle-3" fill-opacity="0.23" sketch:type="MSShapeGroup" transfor
9 x="9.89471277" y="1.82231869" width="3" height="9.13705584"></rect>
1             <rect id="Rectangle-4" fill-opacity="0.51" sketch:type="MSShapeGroup" transfor
0 x="9.97364166" y="25.0863108" width="3" height="9.13705584"></rect>
            <rect id="Rectangle-3" fill-opacity="0.93" sketch:type="MSShapeGroup" transfor
1 x="23.2921811" y="1.77663341" width="3" height="9.13705584"></rect>
1             <rect id="Rectangle-4" fill-opacity="0.44" sketch:type="MSShapeGroup" transfor
1 x="5.05314826" y="20.1867727" width="3" height="9.13705584"></rect>
2             <rect id="Rectangle-3" fill-opacity="0.86" sketch:type="MSShapeGroup" transfor
1 x="28.2126745" y="6.67617143" width="3" height="9.13705584"></rect>
        </g>
3     </g>
1 </svg>
4
1
5
1
6
1
7
1
8
1
9
2
0
2
1
2
2
Given this icon comprises multiple shapes inside a group, and as we’ve learned from
above you can apply  animateTransform  to a group, I added my animation code
before the last closing  </g>  tag:
01
02         </g>
        <animateTransform attributeName="transform"
03             type="rotate"
04             from="0 18 18"
05             to="360 18 18"
06             begin="0s"
07             dur="0.85s"
            repeatCount="indefinite"
08         />
09     </g>
10 </svg>
11
The above code sets the icon to rotate from 0 to 360 degrees, i.e. a full circle.

Revolve Axis
Note that this time I have also included an extra two numbers in both
the  from  and  to  settings. This tells the animation to revolve around a point on the
shape’s own internal X / Y axis of  18 18 , i.e. the center of the shape given it is
36x36 pixels in size.

This produces the following animation:


Again, your in-browser animation will be smoother than the above GIF, so be sure
to check out the real thing. As you can see, it’s very quick and easy to put together
animations you can get solid practical use out of.

Wrapping Up
If you haven’t yet taken a run at SVG animation you should now have the tools you
need to get a good solid start.

Further Reading
 Great Codepen example of animated loader SVGs
 More about how animateTransform works over at W3C.
 Grab yourself a copy of the source for this tutorial, play around with the
examples and have fun creating your own inline SVG animations!
Kezz Bracey
Hi there. I'm a designer and coder who specializes in web design and development and also
works in the areas of game development and digital art. In the web space my focus is on theme
development for WordPress, Ghost or any other themeable platform, and on finding the latest
most efficient, user focused design and dev techniques of the day. In game development I'm
addicted to playing with every different engine, toolset and framework I can find and I could
spend months at a time immersed in level design. And in digital art there's little I love more than
creating realistic style vector pieces, and of course assets for games through original art or photo
manipulation. In short, if it's creative and you can make it digitally with code or design apps, I
love it.

kezzbracey
Sign on the Dotted Line:
Animating Your Own SVG
Signature
by Ian Yates1 May 2015
Difficulty:IntermediateLength:MediumLanguages:
Animation CSSSVG
Animating the stroke of an SVG is perfect for simulating handwriting. Over the
course of two tutorials we’re going to use CSS animation to make a signature neatly
appear to be written, as though you’re signing the page yourself.

Here’s what we’ll be building:

1. SVG File
Before we dive into any code, we’re going to need an SVG version of your signature.
It doesn’t matter what software you use to make this, but try to keep the lines and
curves as smooth as possible for best effect. 

Here’s mine, which you can see is drawn with three separate paths:
Fir
st path

Se
cond path
Th
ird path
Make sure your artboard is cropped tightly to the signature, then save the file as an
SVG.

2. Tidying the SVG Code


Opening the file in a code editor will reveal the SVG’s XML structure. Depending on
which application you used to design it, plus how you saved or exported it, you’ll
have an  <svg>  element with some mumbo jumbo before it. The mumbo jumbo can
be removed.

In this case, the elements we’re left with look something like:
1 <svg>
2     <line/>
3     <path/>
4     <line/>
</svg>
5
Within our main  <svg>  we have a  <line> , then a  <path> , then another  <line> .
These are the three vectors we drew, differentiated only because, technically, a line
has no curvature, so it’s defined differently to a path in SVG.
3. Add Classes
We’ll need to separately target these vectors with CSS a little later on, so make sure
they each have a suitable class name. The  <svg>  element will likely already have an
id reflecting the layer name in the application it was designed with.
1 <svg id="signature">
2     <line class="stroke-I" />
3     <path class="stroke-an" />
4     <line class="stroke-flourish" />
</svg>
5
I’ve given my vectors class names depending on what they are (the first one is the “I”
in my name, for example).

4. All the Other SVG Attributes


In fairness, your SVG won’t be looking quite this neat. Each of these vectors will
have a load of coordinates, plus several attributes buried within them. The coordinates
will need to stay, but we can remove some of the commonly-used attributes and place
those in our CSS instead, keeping things nice and DRY.

New Project
I’m going to build this using CodePen, but you can use standalone HTML and CSS
documents if you prefer. Paste the SVG code directly into your HTML document.
Then, remove the attributes each of the path and line elements have in common,
placing them instead in the CSS document. For example, you’ll notice attributes like:

 fill="none"

 stroke="#0F436D"

 stroke-width="2"

 stroke-linecap="round"

 stroke-linejoin="round"
 stroke-miterlimit="10"

These can be removed and applied via CSS instead, like so:
1
path,
2 line {
3   fill: none;
4   stroke: #2a3745;
5   stroke-width: 2;
6   stroke-linecap: round;
  stroke-linejoin: round;
7   stroke-miterlimit: 10;
8 }
9
Much cleaner!

5. Begin Animating
In order to animate the strokes of this SVG we’re going to be using a technique first
discussed by Jake Archibald. The idea is as follows: each of these vectors is going to
be given a dashed stroke. We do this by applying a  stroke-dasharray  value within
the CSS:

Dash Length
For each of these vectors we make the  stroke-dasharray  precisely the length of the
path, so each one has a single dash covering its entire length. This takes a bit of trial
and error, but in our case the values look look like this:
01
.stroke-I {
02   stroke-dasharray: 80;
03 }
04  
05 .stroke-an {
06   stroke-dasharray: 360;
07 }
08  
.stroke-flourish {
09   stroke-dasharray: 40;
10 }
11
Now, in order to animate these strokes, we need to offset each of the dashes so that
the gap covers the vector, not the dash. Does that make sense? These illustrations
might help. In this first one, imagine the dashed line is being used to cover the flourish
at the end of the signature.

Now in this one we’ve offset the dash, so it’s the gap which is over the flourish:

Now all we need to do is use CSS to animate from the offset state to the other.
6. Keyframes
CSS animation relies on first defining keyframes. Each keyframe represents states
along a timeline, then our browsers render the animations between them.

Let’s first see how this dash offset can be animated. We’ll use the first stroke, the “I”,
and animate between two states. Begin by setting up some keyframes:
1
@keyframes write1 {
2     0% {
3         stroke-dashoffset: 80;
4     }
5     100% {
        stroke-dashoffset: 0;
6     }
7 }
8
Here we give the keyframes a name ( write1 ) and using shorthand syntax specify that
at the very beginning of the timeline ( 0% ) we want the  stroke-dashoffset  to
be  80 . In other words: the dash, which is exactly 80px long, will be offset
completely.

At the end of the timeline (at  100% ) we want the  stroke-dashoffset  to be  0 , so
the dash is once more covering the vector.

Apply Animation
Now we have our keyframes, let’s attach them to an animation. We add another
declaration to our  stroke-I  rule:
1 .stroke-I {
2   stroke-dasharray: 80;
3   animation: write1 3s infinite linear;
}
4
Here, using the  animation  property, we say that we want to use
the  write1  keyframes defined a moment ago, we want the whole thing to last
exactly  3  seconds, we want the animation to loop infinitely and we want the speed to
be  linear  (so that there’s no acceleration or deceleration).
Here’s what we get:

Note: I’m using Autoprefixer in CodePen which saves me having to use browser
prefixes on the animation stuff.

Apply to All Three Vectors


We need to define two more sets of keyframes ( write2  and  write3 ) for the
remaining vectors in the signature–and we need to offset by the correct dash lengths
we discovered earlier:
01 @keyframes write2 {
    0% {
02         stroke-dashoffset: 360;
03     }
04     100% {
05         stroke-dashoffset: 0;
06     }
}
07
08  
@keyframes write3 {
09     0% {
10         stroke-dashoffset: 40;
11     }
12     100% {
        stroke-dashoffset: 0;
13     }
14 }
15
16
17
Then we need to apply those animations to the remaining two vectors:
1
.stroke-an {
2   stroke-dasharray: 360;
3   animation: write2 3s infinite linear;
4 }
5  
6 .stroke-flourish {
  stroke-dasharray: 40;
7
  animation: write3 3s infinite linear;
8 }
9
Here’s what we get:

Now we’re getting somewhere! Each vector is animating perfectly, in a linear motion
lasting 3 seconds.

Next step? To get them animating in sequence.

7. Sequential Animation
Currently we have three strokes all animating simultaneously. However, we ideally
want the “I” to animate, then the “an”, then finally the flourish at the end. If we were
to visualise that along a timeline it might look like this:
We can actually represent these sections of the timeline perfectly in our CSS
keyframes. For example, the first section (from 0% to 33.3%) is when we want our “I”
to animate, so we alter the keyframes to finish at 33.3% instead of 100%:
1
@keyframes write1 {
2     0% {
3         stroke-dashoffset: 80;
4     }
5     33.3% {
        stroke-dashoffset: 0;
6
    }
7 }
8
Now, given that all three of our animations are the same length (3 seconds) we can
make sure the second doesn’t start until 33.3%, when the first animation is complete:
1 @keyframes write2 {
2     0%, 33.3%  {
3         stroke-dashoffset: 360;
4     }
5     100% {
6         stroke-dashoffset: 0;
    }
7 }
8
Here’s what that gives us:

Completing the Sequence


The first two animations flow nicely, so let’s improve things by getting the second to
finish at 66.6%, at which point the final animation can start. Our keyframes will look
like this:
01
02
03 @keyframes write1 {
04     0% {
        stroke-dashoffset: 80;
05     }
06     33.3% {
07         stroke-dashoffset: 0;
08     }
}
09
10  
@keyframes write2 {
11     0%, 33.3%  {
12         stroke-dashoffset: 360;
13     }
14     66.6% {
15         stroke-dashoffset: 0;
    }
16 }
17  
18 @keyframes write3 {
19     0%, 66.6% {
20         stroke-dashoffset: 40;
21     }
    100% {
22         stroke-dashoffset: 0;
23     }
24 }
25
26
And the sequence will look like this:

Further Refinement
What we have is good, but it isn’t perfect–certainly far from a realistic pen movement.
Each of these three vectors is being drawn over the course of one second, irrespective
of its length. The middle vector is wa-ay longer than the last, so it should logically
take longer to draw. A better timeline might look something like this:
For added realism there’s even a gap between the first vector finishing and the second
beginning. So let’s alter our keyframe values to reflect that:
01
02
03 @keyframes write1 {
04     0% {
        stroke-dashoffset: 80;
05     }
06     20% {
07         stroke-dashoffset: 0;
08     }
}
09
10  
@keyframes write2 {
11     0%, 25%  {
12         stroke-dashoffset: 360;
13     }
14     90% {
15         stroke-dashoffset: 0;
    }
16 }
17  
18 @keyframes write3 {
19     0%, 90% {
20         stroke-dashoffset: 40;
21     }
    100% {
22         stroke-dashoffset: 0;
23     }
24 }
25
26
Finally, let’s speed things up by changing all the  3s  values to  2s . We can also
update the animation declarations so that each one runs just once, not infinitely
looping:
1 animation: write1 2s 1 linear;
You might also want to play with the  linear  value, instead adding some easing such
as  ease-in ,  ease-in-out ,  ease-out  etc. to make the movement less uniform.
What does that all give us?

Next Time
We’ve made great progress and learned a lot along the way! In the next tutorial we’ll
take things a step further, using Waypoints.js to help us control when the animation
takes place. I’ll see you there!

Ian Yates
Web Design editor at Envato Tuts+
My name's Ian; I'm the web design editor round these parts and I also run the translation project.
Ask me anything you like about web design, in any language… I'll find someone who knows the
answer!

snapti

How to Build a Page Scroll


Progress Indicator With jQuery
and SVG
by Jonathan Cutrell30 Apr 2014

Difficulty:IntermediateLength:ShortLanguages:

jQuery SVGUI DesignAnimationUX

Today we will be looking at a few techniques we can use to show scroll


progress for users who are reading a page. This technique is being used on
an increasing number of sites, and for good reason; it provides a contextual
understanding of investment needed to consume a particular page. As the
user scrolls, they are presented with a sense of current progress in different
formats. 
As
seen on ia.net

Today, we will cover two specific techniques you can employ to show scroll
progress, and leave you with a toolset to create your own. Let's get started!

Setting up the Document


First, we will set up a mock document which will act as our post page. We will
be using  normalize.css  and jQuery, as well as a Google font. Your empty
HTML file should look like this:

01 <!doctype html>

02 <html>

    <head>
03
        <title>Progress Indicator Animation</title>
04
        <link rel="stylesheet" href="css/normalize.css">
05
        <link rel="stylesheet" href="css/style.css">
06
    </head>
07     <body>
08         <!-- fake post content goes here -->
09         <script src="js/jquery.min.js"></script>
10
        <script src="js/script.js"></script>
11
    </body>
12
</html>
13

Next, we will add our fake post content:

0 <main>
1     <article>

0         <header>
2
            <h1>
0                 <div class="container">
3
                    How Should We Show Progress While Scrolling a Post?
0
                </div>
4
            </h1>
0
5         </header>

0         <div class="article-content">
6                 <h2 class="lead-in">

0                     <div class="container">
7                         Lorem ipsum dolor sit amet, consectetur adipisicing elit.

0                     </div>
8
                </h2>
0
            <div class="container">
9
                <p>Pellentesque habitant morbi tristique senectus et netus et malesuada f
1 amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
0 Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean ferme
ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id
1 erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus</p
1
                <!-- add your own additional lorem here -->
1             </div>
2
        </div>
1
    </article>
3
    <footer>
1
4         <h3 class="read-next"><small>Read Next:</small><br>How do I Implement a Foobar?</
1
5

1
6

1
7

1
8

1
9

2     </footer>
0 </main>

2
1

2
2

2
3

2
4

2
5

This gives us enough content to test our scrolling behaviors.

Basic Styling
We're going to use some basic styling to make our post a little more attractive.

01 @import url(http://fonts.googleapis.com/css?family=Domine:400,700);

02 body {

    font-size: 16px;
03
}
04
h1,
05
h2,
06
h3,
07 h4,

08 h5,

h6 {
09
    font-family: "Domine", sans-serif;
10
}
11
 
12
h1 {
13
    font-size: 3.5em;
14 }
15  
16 .lead-in {

17     color: #fff;

18     font-weight: 400;

19     padding: 60px 0;

    background-color: #0082FF;
20
}
21
 
22
article header {
23
    border-top: 3px solid #777;
24
    padding: 80px 0;
25 }
26  
27 .article-content {
28     font-size: 1em;

29     font-weight: 100;

30     line-height: 2.4em;

}
31
 
32
p {
33
    margin: 4em 0;
34
}
35

36

37
 
38
.container {
39
    width: 700px;
40
    margin: 0 auto;
41
}
42
 
43
 
44 footer {
45     text-align: center;

46     background-color: #666;

47     color: #fff;

48     padding: 40px 0;

    margin-top: 60px;
49
}
50
 
51
.read-next {
52
    font-size: 2em;
53
}
54

55

56

Scroll Position Calculation


To calculate our scroll position, we need to understand conceptually what we
are tracking. Since JavaScript can track only the top scroll value, we will need
to track our scroll value from 0 (at the top, not scrolled) to whatever the final
scroll value is. That final scroll value will be equal to the total document length
minus the height of the window itself (because the document will scroll until
the bottom of the document reaches the bottom of the window).

We will use the following JavaScript to calculate this scroll position.


01
(function(){
02     var $w = $(window);
03     var wh = $w.height();
04     var h = $('body').height();

05     var sHeight = h - wh;

06     $w.on('scroll', function(){

        var perc = Math.max(0, Math.min(1, $w.scrollTop()/sHeight));


07
    });
08
 
09
}());
10

The above code sets the window height and the body height, and when the
user scrolls it uses those values to set a  perc  variable (short for percentage).
We also utilize  Math.min  and  Math.max  to limit the values to the 0-100 range.

With this percentage calculation, we can drive the progress indicator.

Circle Indicator
The first indicator we will create is an SVG circle. We will utilize the
SVG  stroke-dasharray  and stroke-dashoffset  properties to show progress.
First, let's add the progress indicator to the document.

01 <div class="progress-indicator">

02     <svg>

        <g>
03
            <circle cx="0" cy="0" r="20" class="animated-circle" transform="translate(50,
04
        </g>
05
        <g>
06
            <circle cx="0" cy="0" r="38" transform="translate(50,50)"  />
07
08         </g>

09     </svg>

10     <div class="progress-count"></div>

11 </div>

This markup gives us two circles in an SVG, as well as a containing div to


show our percentage count. We need to add style to these elements as well,
and then we'll explain how these circles are positioned and animated.

01 .progress-indicator {

02     position: fixed;

    top: 30px;
03
    right: 30px;
04
    width: 100px;
05
    height: 100px;
06
}
07 .progress-count {
08     position: absolute;

09     top: 0;

10     left: 0;

11     width: 100%;

    height: 100%;
12
    text-align: center;
13
    line-height: 100px;
14
    color: #0082FF;
15
}
16
 
17 svg {
18     position: absolute;

19 }

20 circle {

21     fill: rgba(255,255,255,0.9);
22

23
}
24
 
25 svg .animated-circle {
26     fill: transparent;

27     stroke-width: 40px;

28     stroke: #0A74DA;

29     stroke-dasharray: 126;

    stroke-dashoffset: 126;
30
}
31

32

These styles set us up to animate our circle element. Our progress should
always be visible, so we set position to fixed on the  .progress-
indicator  class, with positioning and sizing rules. We also set our progress

count to be centered both vertically and horizontally inside this div.

The circles are positioned in the center using transform on the SVG elements
themselves. We start the center of our circles using transform. We use a
technique here that allows us to apply a rotation from the center of our circles
in order to start the animation at the top of the circle (rather than the right side
of the circle). In SVG, transforms are applied from the top left of an element.
This is why we must center our circles at  0, 0 , and move the circle's center
to the center of the SVG itself using  translate(50, 50) .

Using stroke-dasharray and stroke-dashoffset


The properties  stroke-dasharray  and stroke-dashoffset  allow us to animate
the stroke of an SVG.  stroke-dasharray  defines the visible pieces of a stroke.
stroke-dashoffset  moves the start of the stroke. These attributes combined

allow us to create a stroke "keyframing" process.

Updating stroke-dasharray on Scroll


Next, we will add a function to update the stroke-dasharray on scroll, using
our percentage progress previously shown.

01

02 (function(){

    var $w = $(window);
03
    var $circ = $('.animated-circle');
04
    var $progCount = $('.progress-count');
05
    var wh = $w.height();
06
    var h = $('body').height();
07     var sHeight = h - wh;
08     $w.on('scroll', function(){
09         var perc = Math.max(0, Math.min(1, $w.scrollTop()/sHeight));

10         updateProgress(perc);

11     });

12  
    function updateProgress(perc){
13
        var circle_offset = 126 * perc;
14
        $circ.css({
15
            "stroke-dashoffset" : 126 - circle_offset
16
        });
17
        $progCount.html(Math.round(perc * 100) + "%");
18     }
19  
20 }());

21

The offset that matches our circle happens to be about 126. It's important to
note that this won't work for all circles, as 126 is about the circumference of a
circle with a radius of 20. To calculate the stroke-dashoffset for a given circle,
mutiply the radius by 2PI. In our case, the exact offset would be  20 * 2PI =
125.66370614359172 .
Horizontal Progress Variation
For our next example, we'll make a simple horizontal bar fixed to the top of the
window. To accomplish this, we'll use an empty progress indicator div.

1 <div class="progress-indicator-2"></div>

Note: we've added the "-2" to allow us to include this example in the same
CSS file.

Next, we'll add our styling for this element.

1 .progress-indicator-2 {
2     position: fixed;
3     top: 0;

4     left: 0;

5     height: 3px;

    background-color: #0A74DA;
6
}
7

Finally, we will set the width of the progress bar on scroll.

1 var $prog2 = $('.progress-indicator-2');

2 function updateProgress(perc){

3     $prog2.css({width : perc*100 + '%'});

4 }

All together, our final JavaScript should look like this:

01 (function(){

02     var $w = $(window);

    var $circ = $('.animated-circle');


03
    var $progCount = $('.progress-count');
04
    var $prog2 = $('.progress-indicator-2');
05

06     var wh = $w.height();
07     var h = $('body').height();

08     var sHeight = h - wh;

09     $w.on('scroll', function(){

10         var perc = Math.max(0, Math.min(1, $w.scrollTop()/sHeight));

        updateProgress(perc);
11
    });
12
 
13
    function updateProgress(perc){
14
        var circle_offset = 126 * perc;
15
        $circ.css({
16             "stroke-dashoffset" : 126 - circle_offset
17         });
18         $progCount.html(Math.round(perc * 100) + "%");

19  

20         $prog2.css({width : perc*100 + '%'});

21     }

22  
}());
23

24

Other Ideas for Progress Bars


This article is intended to give you the tools and inspiration to design your own
scroll progress solutions. Other ideas for progress bars might include using
more descriptive or humanized terms for the progress indication itself, such as
"halfway there" or "just getting started". Some implementations (like
the ia.netexample shown previously) use estimation of an article's read time.
This could be estimated using code similar to the following:

1 var wordsPerMin = 300; // based on this article: http://www.forbes.com/sites/brettnelso


successful/
2
var wordsArray = $(".article-content").text().split(" ");
3 var wordCount = wordsArray.length;
4 var minCount = Math.round(wordCount / wordsPerMin);

You would then use the  minCount  in conjunction with the  perc  variable we
are updating on scroll to show the reader their remaining time to read the
article. Here's a very basic implementation of this concept.

1
function updateProgress(perc){
2     var minutesCompleted = Math.round(perc * minCount);
3     var remaining = minCount - minutesCompleted;
4     if (remaining){

5         $(".progress-indicator").show().html(remaining + " minutes remaining");

6     } else {

        $(".progress-indicator").hide();
7
    }
8
}
9

One Final Piece: Adaptive Screen Sizing


To ensure that our progress indicator works as it should, we should make sure
our math is calculating the right things at the right times. For that to happen,
we need to make sure we are re-calculating the heights and updating the
progress indicator when the user resizes the window. Here's an adaptation of
the JavaScript to make that happen:

01 (function(){
02

03     var $w = $(window);

04     var $circ = $('.animated-circle');

    var $progCount = $('.progress-count');


05
    var $prog2 = $('.progress-indicator-2');
06
 
07
    var wh, h, sHeight;
08
 
09
    function setSizes(){
10
        wh = $w.height();
11         h = $('body').height();
12         sHeight = h - wh;
13     }

14  
15     setSizes();

16  

17     $w.on('scroll', function(){

        var perc = Math.max(0, Math.min(1, $w.scrollTop()/sHeight));


18
        updateProgress(perc);
19
    }).on('resize', function(){
20
        setSizes();
21
        $w.trigger('scroll');
22     });
23
 
24     function updateProgress(perc){
25         var circle_offset = 126 * perc;

26         $circ.css({

27             "stroke-dashoffset" : 126 - circle_offset

        });
28
        $progCount.html(Math.round(perc * 100) + "%");
29
 
30

31         $prog2.css({width : perc*100 + '%'});

32     }

33  

34 }());

35

This code declares a function which sets the variables we need to calculate
the progress at any given screen size, and calls that function on resize. We
also re-trigger scroll on window resize so that our  updateProgress  function is
executed.

You've Reached the End!


Having laid the foundation for any number of variants, what can you come up
with? What progress indicators have you seen that work? How about
indicators that are bad for usability? Share your experiences with us in the
comments!

Potrebbero piacerti anche