Step by step to teach you to use HTML5 SVG to achieve animation effect

Time:2020-1-19

Crazy technology house
Original: https://www.smashingmagazine


The first WeChat public address is jingchengyideng.
Welcome to pay attention and push you fresh front-end technical articles every day


abstract
In this article, you will learn how the awwwards net implements animation. This article introduces the circle element in HTML5 SVG, its stroke attribute, and how to use CSS variables and animate them with vanilla JavaScript.

SVG is an XML based markup language for defining scaling vector graphics。 It allows you to draw paths, curves, and shapes from a set of points defined in a 2D plane. You can also animate these paths by adding dynamic attributes such as stroke, color, thickness, fill, and so on.

From April 2017, CSS Level 3 fill and stroke module began to support setting SVG color and fill pattern from external style sheet instead of setting attributes on each element. In this tutorial, we will use simple pure hexadecimal colors, but fill and stroke properties also support patterns, gradients, and images as values.

Be careful: when visiting the awwwards website, you need to set the browser width to 1024px or higher to better view the animation display.

Step by step to teach you to use HTML5 SVG to achieve animation effect

  • Demo link
  • source code

file structure

Let’s start by creating a file in the terminal:

🌹  mkdir note-display
🌹  cd note-display
🌹  touch index.html styles.css scripts.js

HTML
This is the connection.cssandjsInitial template for file:

<html lang="en">
<head>
  <meta charset="UTF-8">

  <title>Note Display</title>

  <link rel="stylesheet" href="./styles.css">
</head>
<body>
  <script></script>
</body>
</html>

Each note element contains a list item:liUsed for preservationcirclenoteValue and its valuelabel

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: listing item elements and their immediate children:.circle, .percentand.label

.circle_svgIs an SVG element that contains two < circle > elements. The first is the path to fill, and the second is used to prepare the animation.

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: SVG elements: SVG wrapper and round label

Comments are divided into integers and decimals, so you can set them to different sizes of fonts.labelIt’s a simple one<span>。 Put all these elements together and it looks like this:

<li class="note-display">
  <div class="circle">
    <svg width="84" height="84" class="circle__svg">
      <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
      <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
    </svg>

    <div class="percent">
      <span class="percent__int">0.</span>
      <span class="percent__dec">00</span>
    </div>
  </div>

  <span class="label">Transparent</span>
</li>

cxandcyProperty defines the X and Y center points of the circle.rProperty defines its radius.

You may have noticed the underline / dash pattern in the class name. This is BEM (block element modifier), which representsblock, elementandmodifier。 It is a way to make element naming more structured, organized and semantic.

Recommended reading: what is BEM and why it is needed

To complete the template structure, let’s wrap four list items in an unordered list element:

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: the unordered list wrapper has fourliSubelement

<ul class="display-container">
  <li class="note-display">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Transparent</span>
  </li>

  <li class="note-display">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Reasonable</span>
  </li>

  <li class="note-display">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Usable</span>
  </li>

  <li class="note-display">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Exemplary</span>
  </li>
</ul>

You have to ask yourselfTransparentReasonableUsableandExemplaryWhat do labels mean. As you become more familiar with programming, you will find that writing code is not only for the normal operation of the program, but also to ensure that it can be maintained and extended for a long time. These can only be implemented when your code is easy to modify.

AbbreviationTRUEIt should be able to help you determine if your code will adapt to future changes. “

Next time, ask yourself:

Transparency: are the consequences of code changes clear?
Reasonable: is cost-effectiveness worth it?
Available: can I reuse it accidentally?
Example: does it use high quality as an example of future code?

  • Transparent: is the consequence of code modification clear?
  • ReasonableIs the cost-effectiveness worth it?
  • UsableCan I reuse it in different scenarios?
  • Exemplary (example): can it be used as a high quality code template in the future?

Note: Sandi Metz explained in the book “practice guide of object-oriented design: description of ruby language”TRUEAnd other principles, and how to implement them through design patterns. If you haven’t started working on design patterns, consider putting the book on your desk.

CSS

Let’s import fonts and make them work for all:


@import url('https://fonts.googleapis.com/css?family=Nixie+One|Raleway:200');

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

box-sizing: border-boxAttributes include fill and border values to the total width and height of the element, making it easier to calculate the extents of the drawing.

Note: About *box-sizing*Please read “make it easier to use CSS box”.

body {
  height: 100vh;
  color: #fff;
  display: flex;
  background: #3E423A;
  font-family: 'Nixie One', cursive;
}

.display-container {
  margin: auto;
  display: flex;
}

Display by combining rules:bodyMediumflexand.display-containerMediummargin-auto, you can center the child elements vertically and horizontally..display-containerElement will also be used as aflex-container; in this way, its child elements are placed in the same row along the main axis.

.note-displayThe list item will also be aflex-container。 Since many children are centered, we can use thejustify-contentandalign-itemsProperty to complete. Allflex-itemsAre centered vertically and horizontally. If you’re not sure what they are, check the alignment section in the CSS flexbox visualization guide.

.note-display {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 25px;
}

Let’s set up`stroke-widthstroke-opacityandstroke-linecapApply strokes to circles, and these rules move the picture. Next, we add a color to each circle:

.circle__progress {
  fill: none;
  stroke-width: 3;
  stroke-opacity: 0.3;
  stroke-linecap: round;
}

.note-display:nth-child(1) .circle__progress { stroke: #AAFF00; }
.note-display:nth-child(2) .circle__progress { stroke: #FF00AA; }
.note-display:nth-child(3) .circle__progress { stroke: #AA00FF; }
.note-display:nth-child(4) .circle__progress { stroke: #00AAFF; }

In order to position the percentage element absolutely, you must know exactly what these concepts are..circleElement should be a reference, so let’s addposition: relative

Note: for a more in-depth and intuitive explanation of absolute positioning, please read “once and for all understanding of CSS position”.

Another way to center elements is totop: 50%, left: 50%andtransform: translate(-50%, -50%);Combine to position the center of an element at the center of its parent.

.circle {
  position: relative;
}

.percent {
  width: 100%;
  top: 50%;
  left: 50%;
  position: absolute;
  font-weight: bold;
  text-align: center;
  line-height: 28px;
  transform: translate(-50%, -50%);
}

.percent__int { font-size: 28px; }
.percent__dec { font-size: 12px; }

.label {
  font-family: 'Raleway', serif;
  font-size: 14px;
  text-transform: uppercase;
  margin-top: 15px;
}

So far, the template should look like this:

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: completed template elements and styles

Fill transition

You can create circular animation with the help of two circular SVG attributes:stroke-dasharrayandstroke-dashoffset

stroke-dasharrayDefines the dashed gap pattern in the stroke. “

It may require up to four values:

When it is set to a unique integer(stroke-dasharray:10)When, dashes and gaps have the same size;
For two values(stroke-dasharray:10 5), the first applies to dashes, and the second to gaps;
Third and fourth forms(stroke-dasharray:10 5 2andstroke-dasharray:10 5 2 3)Dashes and gaps of various styles will be generated.

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure:stroke-dasharrayAttribute value

Image display properties on the leftstroke-dasharraySet to 0 to 238px of circumference length.

Second image representationstroke-dashoffsetProperty, which offsets the beginning of the dash array. Its value range is also from 0 to the circumference length.

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure:stroke-dasharrayandstroke-dashoffsetattribute

To produce a filling effect, we willstroke-dasharraySet to the circumference so that all of its length fills its sprint range without gaps. We also cancel it out with the same value, which makes it “hidden.”. Then?stroke-dashoffsetUpdates to the corresponding description text, populating its journey based on the transition duration.

Property updates are done in the script through CSS variables. Let’s declare variables and set properties:

.circle__progress--fill {
  --initialStroke: 0;
  --transitionDuration: 0;
  stroke-opacity: 1;
  stroke-dasharray: var(--initialStroke);
  stroke-dashoffset: var(--initialStroke);
  transition: stroke-dashoffset var(--transitionDuration) ease;
}

To set the initial value and update the variables, let’s use thedocument.querySelectorAllSelect all.note-displayElement start. At the same timetransitionDurationSet to 900 Ms.

Then, we walk through the display array and select its.circle__progress.circle__progress--fillAnd extract therProperty set to calculate the perimeter. With it, we can set the initial--dasharrayand--dashoffsetValue.

When--dashoffsetVariable bysetTimeout When you update, the animation occurs:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let progress = display.querySelector('.circle__progress--fill');
  let radius = progress.r.baseVal.value;
  let circumference = 2 * Math.PI * radius;

  progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);
  progress.style.setProperty('--initialStroke', circumference);

  setTimeout(() => progress.style.strokeDashoffset = 50, 100);
});

To transition from the top, you have to rotate.circle__svgElements:

.circle__svg {
  transform: rotate(-90deg);
}

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: stroke attribute transformation

Now, let’s calculate thedashoffsetValue. Note value will passdata-*Property insert eachliProject. *You can replace it with any name that meets your needs, and then retrieve it in the metadata set through the data set of the element:element.dataset.*

Note: you canMDN Web DocsI’ve got something to do with itdata-*Property.

Our properties will be named“data-note”:

<ul class="display-container">
+ <li class="note-display" data-note="7.50">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Transparent</span>
  </li>

+ <li class="note-display" data-note="9.27">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Reasonable</span>
  </li>

+ <li class="note-display" data-note="6.93">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Usable</span>
  </li>

+ <li class="note-display" data-note="8.72">
    <div class="circle">
      <svg width="84" height="84" class="circle__svg">
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"></circle>
        <circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"></circle>
      </svg>

      <div class="percent">
        <span class="percent__int">0.</span>
        <span class="percent__dec">00</span>
      </div>
    </div>

    <span class="label">Exemplary</span>
  </li>
</ul>

parseFloatMethod willdisplay.dataset.noteThe returned string is converted to a floating-point number.offsetIndicates the percentage missing when the maximum value is reached. Therefore, for7.50Note, we will get(10 - 7.50) / 10 = 0.25, which meanscircumferenceThe length should be offset by 25% of its value:

let note = parseFloat(display.dataset.note);
let offset = circumference * (10 - note) / 10;

Update scripts.js:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let progress = display.querySelector('.circle__progress--fill');
  let radius = progress.r.baseVal.value;
  let circumference = 2 * Math.PI * radius;
+ let note = parseFloat(display.dataset.note);
+ let offset = circumference * (10 - note) / 10;

  progress.style.setProperty('--initialStroke', circumference);
  progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

+ setTimeout(() => progress.style.strokeDashoffset = offset, 100);
});

Step by step to teach you to use HTML5 SVG to achieve animation effect

Sroke property converted to note value

Before proceeding, let’s extract the stoke transformation into its own method:

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
- let progress = display.querySelector('.circle__progress--fill');
- let radius = progress.r.baseVal.value;
- let circumference = 2 * Math.PI * radius;
  let note = parseFloat(display.dataset.note);
- let offset = circumference * (10 - note) / 10;

- progress.style.setProperty('--initialStroke', circumference);
- progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

- setTimeout(() => progress.style.strokeDashoffset = offset, 100);

+ strokeTransition(display, note);
});

+ function strokeTransition(display, note) {
+   let progress = display.querySelector('.circle__progress--fill');
+   let radius = progress.r.baseVal.value;
+   let circumference = 2 * Math.PI * radius;
+   let offset = circumference * (10 - note) / 10;

+   progress.style.setProperty('--initialStroke', circumference);
+   progress.style.setProperty('--transitionDuration', `${transitionDuration}ms`);

+   setTimeout(() => progress.style.strokeDashoffset = offset, 100);
+ }

Pay attention to growth value

The other thing is to transfer notes from0.00Convert to the note value to be final. The first thing to do is to separate integers from decimal values. You can use the string methodsplit()。 They are then converted to numbers and passed as parameters toincreaseNumber()Function to display the integer and decimal marks on the corresponding elements.

const displays = document.querySelectorAll('.note-display');
const transitionDuration = 900;

displays.forEach(display => {
  let note = parseFloat(display.dataset.note);
+ let [int, dec] = display.dataset.note.split('.');
+ [int, dec] = [Number(int), Number(dec)];

  strokeTransition(display, note);

+ increaseNumber(display, int, 'int');
+ increaseNumber(display, dec, 'dec');
});

stayincreaseNumber()In function, we choose.percent__intstill.percent__decElement, depending onclassName, and whether the output should contain decimal points. Next, puttransitionDurationSet to 900 Ms. Now, the animation represents a number from 0 to 7, and the duration must be divided by note900 / 7 = 128.57ms。 The result shows how long it will take to increase each iteration. This meanssetIntervalEvery minute128.57msTrigger once.

After setting these variables, definesetIntervalcounterThe variable is attached to the element as text and is incremented each iteration:

function increaseNumber(display, number, className) {
  let element = display.querySelector(`.percent__${className}`),
      decPoint = className === 'int' ? '.' : '',
      interval = transitionDuration / number,
      counter = 0;

  let increaseInterval = setInterval(() => {
    element.textContent = counter + decPoint;
    counter++;
  }, interval);
}

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: count growth

That’s cool! It does increase the count, but it plays in an infinite loop. When the note reaches the value we want, we need to clear itsetInterval。 Can passclearIntervalFunction complete:

function increaseNumber(display, number, className) {
  let element = display.querySelector(`.percent__${className}`),
      decPoint = className === 'int' ? '.' : '',
      interval = transitionDuration / number,
      counter = 0;

  let increaseInterval = setInterval(() => {
+   if (counter === number) { window.clearInterval(increaseInterval); }

    element.textContent = counter + decPoint;
    counter++;
  }, interval);
}

Step by step to teach you to use HTML5 SVG to achieve animation effect

Figure: final completion

Now, the number is updated to the note value and theclearInterval()Function clear.

This is the end of the tutorial. I hope you like it!

If you want to develop something more interactive, check out the memory game tutorial created with vanilla JavaScript. It covers basic HTML5, CSS3, and JavaScript concepts such as positioning, perspective, transformation, flexbox, event handling, timeout, and triples.

I wish you a happy code! A kind of


The first WeChat public address is jingchengyideng.

Step by step to teach you to use HTML5 SVG to achieve animation effect
Welcome to scan the two-dimensional code to pay attention to the public number, push you every day to send fresh front-end technical articles.


Recommended Today

The first Python Programming challenge on the Internet (end)

Date of establishment: March 28, 2020Update Date: April 22, 2020 (end)Personal collection Tool.pywebsite:http://www.pythonchallenge.com/Note: please quote or change this article at will, just mark the source and the author. The author does not guarantee that the content is absolutely correct. Please be responsible for any consequences Title: the first Python Programming challenge on the web Find […]