The writer is Wu Guanxi

0 Opportunity and background

In Q1 this year (the first quarter of 2020), I participated in the front-end development of the business of “Jingxi Factory” of Jingxi Business Division. Through jingxi Factory, users can participate in the “online production” of masks, paper, rice and other goods, which can not only make fun, but also receive free prizes. The event can now be accessed through the “Jingxi” mini program home page.

One month after the launch of the activity, PV reached tens of millions of magnitude, eye-catching. There are many front-end students curious about the animation involved in the realization of the text came into being.

“Jingxi Factory” project includes two platforms: wechat small program native page and H5 page. A large number of CSS animations are used in the project, which can run perfectly on both platforms, and no obvious compatibility problems have been found.

In this paper, some of the animation effects involved in the disc and summary. Teach you the principles and implementation details of CSS animation in a real project.

The screenshot of the activity homepage of “Jingxi Factory” is as follows:

0.0 Computer animation principles

Animation refers to a number of frames of static picture, at a certain speed (such as 16 per second) continuous playback, the naked eye due to visual temporary illusion. It takes at least 10 frames per second to achieve the most basic visual retention effect, compared to 24 frames per second for normal movies and 60 frames per second for normal displays.

The following two GIFs are made up of the same 6 frames, but the playback speed is different. 10 frames/second makes it look like animation, and 2 frames/second makes it feel like a lag.

10 frames per second: 2 frames per second:

How complex animations can CSS do?

1.1 Animation Display

Animation of Small people walking in Jingxi Factory (4 times speed) :

1.2 Animation description and analysis

The whole animation is basically the little man enters the factory from the right, goes around the factory and exits from the right:

There is a walking movement during walking:

Notice when you go from right to left, the little guy turns right; When you go from left to right, the little man goes left:

During the path, there will be several dwell points, each of which will stay for a short period of time to do the action in the work:

2. Why use CSS for complex animations? (SVG, Javascript, CSS)

Let’s start by comparing.

SVG 2.1

SVG native support for SMIL(Synchronized Multimedia Integration Language), SMIL allows you to:

(1) Change the numeric attribute of an element (x, y…)

(2) Rotation

(3) change color attribute < the animate > | | < animateColor > (obsolete)

(4) Object direction and motion path direction synchronization (path animation)

These are all regular animation capabilities, but with some SVG-specific features can produce some fantastic effects, such as stroke animation with Stroke-Dasharray and Stroke-Dashoffset. In addition,

, which is also the path animation SMIL, is much more compatible than the offset-path CSS.

Wechat applet: wechat applet does not support SVG and SMIL.

2.2 Javascript

In theory, Javascript can do any animation. Generally speaking, Javascript animation can be divided into animations that manipulate DOM properties and animations that manipulate canvas API, Both principle is through the window. The requestAnimationFrame () or Windows. The setTimeout () this kind of time control function to achieve every 16.7 ms shows a frame, so as to reach 60 frames per second of animation. In addition, there is the Web Animations API, which opens the browser animation engine to developers and is manipulated by JavaScript. It is one of the most effective ways to support animation on the web in the future, allowing browsers to do their own internal optimizations. But the compatibility is poor.

Micro channel small program: Micro channel small program to achieve their own set of WX Ainmation API, incompatible with Web standards.

2.3 CSS

CSS animations are declarative. When you create a keyframe with @keyFrame, the browser will automatically calculate the change of the frame every 16.7ms. These calculations are not in JS, thus avoiding GC. Another benefit of CSS animation is that translateZ can be used to enable GPU hardware acceleration, and in 2020, CSS animation compatibility is very good.

Unfortunately, CSS is less compatible with the offset-path animation.

Micro channel small program: Micro channel small program support CSS animation.

choose

Considering that the project is mainly running on H5 and wechat small program platform, comprehensive compatibility and my proficiency, I finally choose to use CSS animation.

3 classification of CSS animation

< span style = “box-sizing: border-box; color: RGB (62, 62, 62); font-family: arial, sans-serif;

  • “Linear change animation” refers to the animation timing – function = linear (straight line) | | cubic bezier – timing – function (bezier curve).

  • “Non-linear change animation” means animation-timing = step-timing function(segmentation).

3.1 Animation of linear changes

✿ cubic-bezier.com is a good website for visualization of Bezier curves

3.2 Animation of nonlinear changes

Nonlinear change animation, usually used for “frame animation”. Usually the designer outputs a group of sequence frame pictures as the background image, controls the background-position property during animation, and realizes the transition effect through step-timing function.

What do you mean? Let me give you an example of a little person walking:

There are actually two images, one is left foot and the other is right foot (120 X 160), and I use a tool to combine them into one image (120 X 320).

The code is as follows:

<div class="anim linear"></div>
<div class="anim steps"></div>
Copy the code
.anim{
  width:120px;
  height:160px;
  background-image:url(./spirit.png); /* Composite graph */
  background-position:0 0;
  background-size:100% auto;
}

.liear{
  animation:anim-walk 0.4 s linear 0s infinite;
}

.steps{
  animation:anim-walk 0.4 s steps(1) 0s infinite;
}

@keyframes anim-walk{
  0% {background-position:0px 0px;}
  50% {background-position:0px -160px;}
  100% {background-position:0px 0px;}
}
Copy the code

Effect:

linear: steps:

In the CSS code, we define a set of keyframes called anim-walk, where background-position-y is 0 at 0%, -160 at 50%, and back to 0 at 100%. From the renderings, we can see the influence of different Settings of animation-timing function on the animation effect.

  • The data between 0 and 160 is calculated as 0 ~ 40 ~ -80 ~ -120 ~ -160 ~ -120 ~ -80 ~ -40 ~ 0

  • Steps are nonlinear, so the data between 0 ~ -160 ~ 0 is calculated to be 0 ~ 0 ~ 0 ~ -160 ~ -160 ~ -160 ~ -160 ~ 0

3.3 Path Animation (CSS how to do curve path animation?

In the Kyung Hee factory project, the minions move around a few points in the factory.

3.3.1 CSS offset – the path

The easiest way to do this is to use offset-path, which is described in detail in Xinxu Zhang’s article: offset-path-css-animation.

The disadvantage is poor compatibility, which will not be explained in detail here.

3.3.2 Using the time function as the side effect of Bessel curve

In the project of Jingxi Factory, the movement path of the figures can be seen from the following setting diagram. The marked dots are to stay and work.

To be sure, the position of these dots in the CSS animation must be a keyframe, and the straight path between the dots is easy to do, but what about curves?

Here I use “CSS layered animation” and “time function as a side effect of bezier curve”. Simply put, by animating two or more elements (layering), we can have more granular control over the path of an element, using one timing function along the X-axis and another timing function along the Y-axis.

Let’s say I have A[0,0] and B[100,100]. When we go from A to B, we can break it down into our change in X, and our change in Y. When a line moves, the cumulative change in X and Y is the same:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
Copy the code
.anim-x{
  animation: anim-x 1000ms 0s linear infinite;
}
.anim-y{
  animation: anim-y 1000ms 0s linear infinite;
}
@keyframes anim-x {
  0%{ transform:translate3d(0, 0, 0)} 100%transform:translate3d(100px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0, 0, 0)} 100%transform:translate3d(0 , 100px , 0) }
}
Copy the code

Conversely, if the cumulative change in the X-axis and Y-axis are not the same, the curve will be off:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
Copy the code
.anim-x{
  animation: anim-x 1000ms 0s ease-in infinite;
}
.anim-y{
  animation: anim-y 1000ms 0s ease-out infinite;
}
@keyframes anim-x {
  0%{ transform:translate3d(0, 0, 0)} 100%transform:translate3d(100px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0, 0, 0)} 100%transform:translate3d(0 , 100px , 0) }
}
Copy the code

This article explains the principle in great detail: CSS layered animations can move elements along curved paths

3.4 Combination

The path animation problem is solved, and the frame animation of the little man walking and working is ready. Here are two more small problems:

(1) The frame animation of the little person walking and working cannot appear at the same time.

(2) When the path animation moves from left to right, the orientation of the villain should be opposite to that when it moves from right to left.

The solution here is also “CSS layered animation” and “non-linear animation”.

Add another layer of turn animation, a layer of control “little man walking frame animation” animation, a layer of control “little man working frame animation” animation, these three control animation are “nonlinear animation”.

The code might look something like this:

<div class="anim-turn">
  <div class="anim-walk"></div>
  <div class="anim-work"></div>
</div>
Copy the code
.anim-turn{ animation: anim-turn ${allTime}ms 0s steps(1) infinite; }. Anim-walk {animation: anim-walk-opacity ${allTime}ms 0s steps(1) infinite, Anim-walk 0.4s steps(1) 0s infinite; }. Anim-work {animation: anim-work-opacity ${allTime}ms 0s steps(1) infinite, Anim-working 0.4s steps(1) 0s infinite; } @frames anim-turn {// transform:scale(1,1)} // transform:scale(-1,1)} // transform:scale(-1,1) {transform:scale(1,1)} // forward} @keyframes anim-walk-opacity {return :1} 50% {return :0} 100% {opacity:1}} @keyframes anim-work-opacity {opacity:0} 50% {opacity:1} 100% {opacity:0}}Copy the code

Add layered CSS for X and Y:

<div class="anim-x">
  <div class="anim-y">
    <div class="anim-turn">
      <div class="anim-walk"></div>
      <div class="anim-work"></div>
    </div>
  </div>
</div>
Copy the code
.anim-x{
  animation: anim-x ${allTime}ms 0s linear infinite;
}
.anim-y{
  animation: anim-y ${allTime}ms 0s linear infinite;
}
.anim-turn{
  animation: anim-turn ${allTime}ms 0s steps(1) infinite;
}
.anim-walk{
  animation: anim-walk-opacity ${allTime}ms 0s steps(1) infinite.anim-walk 0.4s steps(1) 0s infinite;
}
.anim-work{
  animation: anim-work-opacity ${allTime}ms 0s steps(1) infinite.anim-working 0.4s steps(1) 0s infinite;
}

@keyframes.Copy the code

3.5 Write a visual tool to improve efficiency

This is simply the frame of the animation, but the actual animation data, @keyframes, is the key, and these keyframes are definitely not hand-written.

He that would do a good job must sharpen his tools.

So let’s build a visualization tool [DOGE] with Vue.

Something like this:

The basic operations are “Add keyframes,” “Adjust the properties of each keyframe,” “Generate test animations,” and “Output animation CSS.”

“Add Key Frame” :

“Adjust properties for each keyframe” :

Generate Test Animation – Output Animation CSS:

Skip the actual implementation of the tool, here are some key details:

(1) How to draw the animation path?

(2) How to calculate the animation time?

3.6 Draw the animation path

In path animation, every two key frames to determine the beginning and end of the element for a period of time, while time function determines the time variation of the X axis and Y axis, we this time can be divided into N side, then respectively calculate the n-terminal end time when the position of the element, with a straight line connected can get an approximate curve.

Here’s an example:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
Copy the code
@keyframes anim-x {
  0%{ transform:translate3d(0, 0, 0);animation-timing-function:linear}
  100%{ transform:translate3d(300px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0, 0, 0);animation-timing-function:cubic-bezier(0, 26, 74, 1)} {100%transform:translate3d(0 , 300px , 0) }
}
.anim-x{
  animation: anim-x 1000ms 0s;
}
.anim-y{
  animation: anim-y 1000ms 0s;
}
Copy the code

In this example, the X-axis of the element is 0 ~ 300, the animation-timing function is linear, and the Y-axis is 0 ~ 300. Animation-timing function is cubic- Bezier (0,.26,.74,1), and then the time length is defined as 1, evenly divided into 100 segments, and X and Y of different progress are calculated using for loop:

  const moveTo = [0.0];
  const step = 100;
  const dX = 300;
  const dY = 300;
  const timeFunX = "linear";
  const timeFunY = "Cubic - the bezier (, 0,. 26, 74, 1)";
  if (Math.abs(dX) > 0 || Math.abs(dY) > 0) {
    ctx.moveTo(moveTo[0],moveTo[1]);
    for(let i = 0; i <= step; i ++) {const x = getTimeFunctionValue(timeFunX,i/step) * dX + moveTo[0]; // Find x in timeFunX(linear)
      const y = getTimeFunctionValue(timeFunY,i/step) * dY + moveTo[1]; TimeFunY (cubic-bezier(0,.26,.74,1))
      ctx.fillRect(x, y, 1.1);
      if (i % 10= = =0) {
        ctx.font = "16px serif";
        ctx.fillText(` (${x}.${(y).toFixed(2)}) `, x + 20, y + 20); }}}Copy the code

The effect is as follows:

What’s left is getTimeFunctionValue(time function, schedule [0,1]).

The first thing to do is to separate Linear from the rest of the Bezier curve, which is a straight line that returns the same value as any time progress input.

function getTimeFunctionValue(timeFunctionName = "linear",x = 0){... if (timeFunctionName ==="linear") returnx; . }Copy the code

What about Bessel curves? First, let’s learn the time function of the Bezier curve in CSS animation.

3.7 Bezier curve time function in CSS animation

A Bessel curve is a parameter function. The cubic Bezier curve is widely used in computers.

Four points, P0, P1, P2, and P3, define a cubic Bezier curve in the plane or in three dimensions. It goes from P0 to P1, and it goes from P2 to P3. Usually you don’t go through P1 or P2; These two points are just there to provide directional information. The distance between P0 and P1 determines how far the curve goes in the direction of P1 before turning towards P2.

The parameter form of the curve is:

The bezier curve time function in CSS animation is a simplified version of “cubic bezier curve”, where P0 is fixed as [0,0] and P3 is fixed as [1,1].

Moreover, the cartesian coordinate system is different from the geometric coordinate system (x,y), but has other meanings. The horizontal axis represents “time”, with the value of 0% ~ 100%. The vertical axis represents the “progression” of attributes, which can be [0-100%] and can be reduced to less than 0% or greater than 100%.

So this simplified version of the CSS bezier curve can be represented by the following two equations (substitute P0[0,0] P3[1,1]) :

T (time progress) =…

P (% change) =…

Cubic – Bezier (0,.26,.74,1) parameters are actually (P1_time,P1_progression,P2_time,P2_progression). As shown in the figure below:

Take the parameters (P1[0,0.26],P2[0.74,1]) in Cubic – Bezier (0,.26,.74,1) and substitute them into the above two formulas to get the following results (equation) :

T (time progress) =…

P (% change) =…

T in the first equation is the time progress, is the input, solve for the root of this cubic function of T with one variable, plug it into the second equation, and you get P. P is the “degree of change” in the time schedule of T.

Note: A cubic function has three roots, but only real numbers and positive solutions between [0 ~ 1].

3.8 How to calculate time in animation?

Above, we use the method of integration to approximate the animation path, which means that we can approximate the length of the animation path. Length/speed = animation time. The speed can be customized.

4 other

4.1 Resolved the problem of incorrect hierarchy (translateZ)

Jingxi factory also has a conveyor belt animation, you can see the original version below:

In the picture above, you can see the goods moving from left to right along the conveyor belt. At first, the left side looked quite normal, but on the right side, the rear cargo would cover the front cargo.

The reason is simple, because the items are side-by-side elements, and the latter element is always higher than the previous one. Something like this:

<div>
  <div>1</div>  <! -- Display lowest level -->
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>  <! -- Display the highest level -->
</div>
Copy the code

But this animation wants the hierarchy to be high in the middle and low on the sides.

Some of you might want to use z-index, but z-index doesn’t work in CSS animations.

The correct solution is to convert it to A 3D display with translateZ to achieve a high in the middle and low on both sides:

@keyframes anim-z{
  0% {transform: perspective(500px) translateZ(0);}
  50% {transform: perspective(500px) translateZ(50px);}
  100% {transform: perspective(500px) translateZ(0);}
}
Copy the code

Increased effect:

4.2 Solve the problem of frame by frame animation shake

There is also the problem of shaking the frame animation. If you look at the GIF above, you can see that the person is shaking a little bit. This GIF is shown on an iPhone 6 Plus (414 pixels on the screen).

The problem lies in unit conversion: rem is usually used in the adaptation of mobile terminal, while RPX is used in the small program. In the process of their calculation into PX, there may be a problem of rounding, resulting in frame animation shaking.

For a study of frame-by-frame animation dithering, look no further than this article from Concave and Convex Lab: CSS Tips: A Frame-by-frame Animation Dithering Solution

The paper proposes three plans A, B, and C, with plan C being the “ultimate solution”. Unfortunately, this scheme uses SVG, and applets do not support SVG.

As the next best thing, I chose option A, which uses CSS media queries to write breakpoints in px units.

/* iphone5 (320) */
@media screen and (min-width: 300px) and (max-width: 349px) {
    .m_worker_employee {
        width:51px;
        height: 68px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -204px50%} {background-position: 0px -272px100%} {background-position: 0px -204px}}}/* iphone6 (375) */
@media screen and (min-width: 350px) and (max-width: 399px) {
    .m_worker_employee {
        width:60px;
        height: 80px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -240px50%} {background-position: 0px -320px100%} {background-position: 0px -240px}}}/* iphone6P (414) data between 400 and 449 */
@media screen and (min-width: 400px) and (max-width: 449px) {
    .m_worker_employee {
        width:66px;
        height: 88px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -264px50%} {background-position: 0px -352px100%} {background-position: 0px -264px}}}Copy the code

After the breakpoint is applied, the frame animation stops shaking.

There are many front-end animation schemes, but choose the most appropriate one according to the environment you are using. Debugging animation is tedious, the key is to use the right tools, if not build your own.

5 Formula correlation

The formula for this article is to use “TeX” software and then output SVG using “MathJax”, which is recommended here: www.mathjax.org/#demo


If you think this post is valuable to you, please like it and follow us on our website and our WecTeam account, where we will post quality articles every week: