Without further ado, let’s go to the effect picture first:

Isn’t it cool that this effect is implemented using SVG+CSS? Now let’s deconstruct the implementation of this animation.


Preliminary knowledge

A few SVG tag usages to master

  • <path>

    instruction parameter instructions
    M x y Move the brush to the point (x,y)
    L x y The brush draws the line segment from the current point to the point (x,y)
    H x The brush draws the horizontal line segment from the current point to the point (x,y0)
    V y The brush draws the vertical line segment from the current point to the point (x0,y)
    A rx ry x-axis-rotation large-arc-flag sweep-flag x y The brush draws an arc from the current point to the point (x,y)
    C x1 y1, x2 y2, x y The brush draws a cubic Bezier curve from the current point to the point (x,y)
    S x2 y2, x y Special version of cubic Bezier curves (omitting the first control point)
    Q x1 y1, x y Draw the quadratic Bezier curve to the point (x,y)
    T x y Special version of quadratic Bessel curve (omitting control points)
    Z There is no parameter Draws a closed graph, if the D attribute does not specify the Z command, draws a line segment instead of a closed graph.

    There are nine instructions for path, but we only use two of them in our animation: A and Z, so let’s focus on A here!

    Rx ry x-axis-rotation Large-arc-flag sweep-flag x y

    Given two points (a starting point and an ending point) and a radius, four arcs can be drawn. These four arcs can be divided in two ways:

    1. According to the view points
      • More than 180 °
      • Less than 180 °
    2. According to the brush direction
      • Clockwise direction
      • Counterclockwise direction

  • Rx,ry is the length of the semi-major axis and semi-minor axis of the arc

  • The x-axis-rotation rotation is the Angle of the arc. It is the Angle between the semi-major axis of the arc and the horizontal direction. Positive numbers represent clockwise rotation.

  • Large-arc-flag 1 indicates a large-angle arc, and 0 indicates a small-angle arc.

  • A sweep-flag of 1 represents the clockwise direction of the arc from the beginning to the end around the center, and 0 represents the counterclockwise direction.

  • X and y are the terminal coordinates of the arc.

  • <defs>

    SVG allows us to define graphic elements that need to be reused later. It is recommended that all reference elements that need to be used again be defined within the DEFS element. Doing so increases the legibility and accessibility of SVG content. Graphic elements defined in defS elements are not rendered directly. You can render these elements using elements anywhere in your viewport. – the MDN

    The purpose of this tag is simply to define a drawing template that we can use directly elsewhere.

    Here we can define our gradient element: linearGradient.

  • <linearGradient>

    The linearGradient element is used to define a linearGradient, which is used to fill or stroke graphic elements. – the MDN

    The gradient of color on a gradient is defined by the stop element.

    Such as:

    <stop offset="5%" stop-color="#F60" />
    Copy the code

    This line of code means: the color is #F60 5% offset from the starting position.

    When we reference our own gradient to set the fill color or brush color, we only need to use the URL () to link to the ID of the gradient, as follows:

    stroke="url(#linear)"
    Copy the code

    Here we set the color of the brush to a gradient with ID Linear.

There are also two properties:

  • stroke-dasharray

    Used to create dashed lines:

    stroke-dasharray = '10'
    stroke-dasharray = '10, 10'
    stroke-dasharray = '10, 10, 5, 5'
    Copy the code

Drawing dashed lines: With one parameter: indicates the length of a dashed line and the distance between each dashed line.

Two or more parameters: one for length and one for spacing

  • stroke-dashoffset

    The stroke-Dashoffset property specifies the distance from the dash mode to the start of the path

    If a percentage value is used, it represents a percentage of the current viewPort.

    The value can be negative. – the MDN

    Simply put, stroke-dashoffset sets the offset of the brush’s starting point, which is positive to the left.

The specific implementation

Draw the SVG path

By tracing points, I traced SVG from an apple image downloaded from the Internet. It may not be accurate, but it should be enough. Here is the graph I used to trace the points:

<path
  stroke-linecap="round"
  fill="none"
  stroke-width="10"
  stroke="url(#linear)"
  d=M 197,148a 87,87,0,0, 79,214 A 282,282,0,0, 148,438 A 54,54,0,0, 212,448 A 87,87,0,0,1,288,448 A 54,544,0,0,352,438 A 282,282,0,0, 407,350 A 87,87,0,0,1,413,189 A 87,87,0,0, 303,148 A 141,141,0,0,1,197,148 Z"
/>
<path
  stroke-linecap="round"
  fill="none"
  stroke="url(#linear)"
  stroke-width="10"
  d="M 237,141 A 87,87,0,0,0,314,64 A 87,87,0,0,0,237,141 Z"
/>
Copy the code

And I set my brush color to the gradient ID I’m going to write.

Defining gradient

<defs>
  <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="100%">
    <stop offset="0%" stop-color="rgb(98, 180, 76)" />
    <stop offset="20%" stop-color="rgb(242, 179, 61)" />
    <stop offset="40%" stop-color="rgb(238, 125, 55)" />
    <stop offset="60%" stop-color="rgb(205, 58, 71)" />
    <stop offset="80%" stop-color="rgb(142, 61, 140)" />
    <stop offset="100%" stop-color="rgb(39, 155, 213)" />
  </linearGradient>
</defs>
Copy the code

To display it in color, I set up six gradient color nodes.

Note that even when the gradient color is applied to the brush, it fills the color of the surface. So for example, I defined linearGradientx1=”0%” y1=”0%” x2=”100%” y2=”100%”To make the page color appear as a gradient from the top left to the bottom right, rather than a gradient from the beginning to the end of the path, so we define gradients at 0% and 100% colors that do not need to be the same. (We can’t actually define the gradient from the start to the end of the path either.)

JavaScript gets the path length

let pathLength = document.querySelectorAll('path');
pathLength.forEach((item, index) = > {
  let itemLength = Math.ceil(item.getTotalLength());
  console.log(itemLength);
});
Copy the code

Here we use a JavaScript API: getTotalLength(), which gets the path length so we can set the offset.

Set the animation

path {
  animation: dash 5s linear forwards;
}
path:nth-child(2) {
  stroke-dasharray: 1162;
  stroke-dashoffset: -1162;
}
path:nth-child(3){
  stroke-dasharray: 236;
  stroke-dashoffset: -236;
}
@keyframes dash {
  to {
    stroke-dashoffset: 0; }}Copy the code

Our animation is a process of creating something from nothing, so we set the length of the dotted line and the space * between each dotted line to the length of the path.

In this case we should display the full Apple logo.

When we set stroke-Dashoffset to the path length, we hide the dotted part of the picture completely before the brush start. The whole picture will be empty!

At this point, we add animation for the two paths, and let them change stroke-Dashoffset to 0 uniformly within 5s, so what we see is a process of drawing logos dynamically, as shown in the previous GIF.

Other implementations similar to animation

First, the effect picture:

The implementation code is as follows:

<svg
xmlns="http://www.w3.org/2000/svg"
width="200px"
height="164.50 px"
class="icon"
version="1.1"
viewBox="0 0 1245 1024"
id="logo-pic"
>
<defs>
  <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
    <stop offset="0%" stop-color="#05a" />
    <stop offset="100%" stop-color="#0a5" />
  </linearGradient>
</defs>
<path
  fill="url(#linear)"
  d="M870.953514 333.768649 C-209.781622 0-374.867027 145.020541-374.867028 322.836756s165.223784 322.836757 374.867028 322.836757a548.116757 548.116757 0 0 132.289729-22.417297L1124.185946 1024L-33.210811-110.702703 C1179.537297 845.768649 1245.405405 756.92973 1245.405405 656.605405 C0-177.816216-176.294054-322.836757-374.451891-322.836756-Z M-121.496217 267.208648A47.463784 47.463784 0 0 1 705.72973 556.419459 47.740541 47.740541 0 0 1 749.457297 512C33.349189 0 55.351351 22.278919 55.351352 44.557838 S -22.002162 44.419459-55.351352 44.419459 Z M242.438919 0a47.325405 47.325405 0 0 1-43.727567-44.557838A47.602162 47.602162 0 0 1 991.896216 512c33.072432 0 55.351351 22.278919 55.351352 44.557838 s to 22.278919 44.419459 55.351352 44.419459 z"
/>
<path
  fill="url(#linear)"
  d="M440.735135 0c198.434595 00 166.054054 0 378.326486c0 122.188108 66.006486 222.512432 176.432432 300.41946 L-44.142702 133.811892 154.153513-77.907027c55.351351 10.931892 99.355676 22.278919 154.430271 22.278919 13.837838 0 27.675676 0 41.513513-1.798919a334.045405 334.045405 0 0 1-13.837838-93.267027c0-193.72973 166.054054-352.034595 374.728649-352.034595a378.88 378.88 0 0 1 42.482162 2.629189C847.429189 133.12 657.574054 0 440.735135 0zm294.192432 296.683243a53.137297 53.137297 0 1 52.722163-53.137297 52.860541 52.860541 0 0 1-52.722163 53.137297z m314.395676 0a53.137297 53.137297 0 1 52.722162-53.137297A52.860541 52.860541 0 0 1
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="2rem"
id="logo-name"
>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-1"
>
  WeChat
</text>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-2"
>
  WeChat
</text>
<text
  text-anchor="middle"
  x="50%"
  y="50%"
  class="logo logo-name-text-3"
>
  WeChat
</text>
</svg>
Copy the code

Note that text in SVG can also be colored by separating the border from the fill. So here we set the border of the text to the gradient we originally defined, and the fill to None.

#logo-pic {
  width: 75px;
  height: 75px;
}
.logo {
  font-size: 1.25 rem;
  font-weight: bold;
  fill: none;
  stroke-width: 1px;
  stroke-dasharray: 30% 70%;
  animation: stroke 4.5 s infinite linear;
}
.logo-name-text-1 {
  stroke: rgb(0, 169, 86);
}
.logo-name-text-2 {
  stroke: rgb(0, 127, 129);
  animation-delay: -1.5 s;
}
.logo-name-text-3 {
  stroke: rgb(0, 86, 168);
  animation-delay: -3s;
}
@keyframes stroke {
  to {
    stroke-dashoffset: -100%; }}Copy the code

The author specially created a repository on Github, which is used to record the skills, difficulties and easy mistakes in learning full stack development. Please click the link below to browse. If you feel good, please give a little star! 👍


2019/04/02

AJie