1

This article will use the Motion Factory to build and generate various particle animations from 0-1, using the ability of visual editing, and you can achieve it through simple operations. Below I will show you how to achieve it step by step. A particle Action, to be precise, implements a custom action according to a business.

After reading this article, you will know the following:

  1. How to implement a custom action
  2. How to use Motion Factory

Motion Factory

Before implementing the custom action, let me briefly introduce the background of our project. What is a Motion Factory?

Our team uses a lot of dynamic effect scenes, so we precipitated some dynamic effects, and at the same time, we can better reuse them next time. So how many dynamic effects we can support, or which dynamic effects can be reused, we can find them in the dynamic effect library of the dynamic effect factory.

At this time, some students will say that these things do not meet the needs and are not universal, so there is a custom action to be discussed next, that is, to implement an animation that the animation library does not have, and then submit it to the animation library , there is a similar dynamic effect later, others can reuse it.

particle animation

Here, I will take you to implement a particle animation. First of all, let me explain what particle animation is? ? ? ?

particle animation is an animation composed of a large number of particles randomly generated within a certain range to generate motion. It is widely used in simulating weather systems, smoke and light effects, etc. In the micro-gamification scene of the e-commerce platform, particle animation is mainly used to present special effects during energy collection and gold coin collection.

Here I have prepared two examples for you to use in our business scenarios:

飞书20220309-110541.gif

飞书20220309-111311.gif

The particles above are still not enough, and the corresponding textures are attached to the particles, and then displayed in some kind of animation, so as to achieve a visually very cool state, that is, there is a lot of feeling!

Create particle class

In fact, no matter what kind of particle effect, you don't know how to analyze this animation just by looking at the effect. In fact, when analyzing animation, we can first see how a particle changes. Let's look at the picture below:

Particle A is on the canvas. After a certain animation from the position of A, it may be a linear change, or it may be a parabola, or our third-order Bezier curve changes. But everything is the same. In essence, both x and y change. However, x runs from x0 to x1, and the y direction moves from y0 to y1. In fact, for the current particle, it is necessary to have vx and vy. Attributes. The most basic properties of such a particle are actually sufficient for basic animation. We write the following code:

class Particle { 
  constructor(x = 0, y = 0) { 
    this.x = x 
    this.y = x 
    this.radius = 1 
    this.vx = 0 
    this.vy = 0 
    this.color = 'hsla(197,100%,50%,80%)' 
  } 
  
  draw(ctx) { 
    ctx.save() 
    ctx.fillStyle = this.color 
    ctx.beginPath(); 
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); 
    ctx.fill(); 
    ctx.closePath(); 
    ctx.restore() 
  } 
  
  move() { 
    this.x += this.vx 
    this.y += this.vy 
  } 
} 

In this way, we can actually construct the characteristics required by the current particle. But at this time we still need a particle manager to manage all the particles, mainly for

  1. control the number of particles
  2. Generate multiple types of particles
  3. perform a rendering operation
  4. Controls which interval the particle's x-direction velocity and y-direction velocity are in

In fact, these are the so-called unified management, and the code will not be displayed. In fact, these so-called operations above constitute some configurations of particle actions.

ACTIONWriting

We open to this directory

Here are all the actions of our animation factory. For example, I am actually creating a new one here particle-action.ts

Here's a look at how the most basic structure of the next action is implemented.

That is, the actionConfig here is actually what properties correspond to the visual view of the dynamic factory. We will have some default properties, that is, some fixed properties for each action, but at the same time, there will be special cases. Then you are exporting an Action function

Let's see how it's written here, it's actually a fixed way of writing here. But I still want to make it clear:

First of all, the action must act on the view. In the dynamic effect factory, it is actually the corresponding View.

Then there are some easing animations, some parameters of , let's look at the code

export const particleAction: Action<particleActionConfig> = ( 
  view: View, 
  { timing, color, radius, duration, ...config }, 
) => { 
  let ctx: CanvasRenderingContext2D | null 
  return (time: number) => { 
    const progress = useProgress(time, timing, duration, config) 
    useOnce(() => { 
      ctx = view.makeCanvas().getContext('2d') 
    }) 
    if (ctx) { 
      view.addLayer(ctx.canvas) 
    } 
  } 
} 

2 separate hooks are used here

useOnce and useProgress

I have to mention here, why use hooks, the main purpose is to facilitate the linkage of actions with frames when they are called (here borrows the idea of React hooks)

Set or get the duration of the current frame, I will briefly introduce a few to you

useCache

Cache variables to keep the execution of the action

useState

Save and modify the state of an action

useOnce

It will only be executed once in the entire life cycle of the Frame.

useFinish

Executed when the Frame is removed

useContext

Get the current wind of the frame.

useProgress

Get the current progress

useOnce

In fact, it is a bit similar to a singleton, we only create it once, because the above function is called every frame, so in order to avoid the problem of repeated creation

I will briefly say here, this line of code

ctx = view.makeCanvas().getContext('2d')

In fact, view is just something we abstracted. No matter what you do, that is, rendering the screen, it depends on canvas. Therefore, the view has the method of makeCanvas instance, and all subsequent actions are rendered on this canvas to form animation.

2d is actually the simplest and most commonly used method, but canvas is not only 2d but also webgl and webgl2.

actually means that it is OK to expand the 3D capability of our thing. In fact, I have also made some attempts, but since there are not many business scenarios, I also show it myself.

Here I can share with you the animations I made with our Motion Factory 3D.

Alright, let's get down to business, let's move on to writing our particle actions

Let's look at this picture:

A particle moves in a fixed view. If the boundary is moved, it is actually how the speed changes at this time. In fact, it is the so-called collision detection.

I actually have an elastic coefficient here. When the particle moves to the boundary, the vx direction and the vy direction of the particle become opposite directions. If the collision detection is not turned on, it is actually to change the position of the particle and set the x and y values of the particle. in the corresponding location. see the picture below

I only have one direction. code show as below:

// Impact checking

 checkWall(isWallCollisionTest = true, bounce = 1, W = 1000, H = 1000) { 
        if (isWallCollisionTest) { 
            if (this.x + this.radius > W) { 
                this.x = W - this.radius; 
                this.vx *= -bounce; 
            } else if (this.x - this.radius < 0) { 
                this.x = this.radius; 
                this.vx *= -bounce; 
            } 
 
            if (this.y + this.radius > H) { 
                this.y = H - this.radius; 
                this.vy *= -bounce; 
            } else if (this.y - this.radius < 0) { 
                this.y = this.radius; 
                this.vy *= -bounce; 
            } 
        } else { 
            if (this.x - this.radius > W) { 
                this.x = 0; 
            } else if (this.x + this.radius < 0) { 
                this.x = W; 
            } 
            if (this.y - this.radius > H) { 
                this.y = 0; 
            } else if (this.y + this.radius < 0) { 
                this.y = H; 
            } 
        } 
    } 

We set 100 particles to see the effect of collision detection:

The stuttering here is that the stuttering has nothing to do with the rest of us when recording video. When the number of canvas particles is not enough, it will not freeze.

text particles

Then we expanded on the current particle and expanded the text particle. In fact, here is a brief introduction. The entire canvas of canvas is actually pixels. The principle we can implement is actually very simple. There is a getImageData method in Canvas, which can get all pixel data in a rectangular range. So let's try to get the shape of a text.

The data property returns a Uint8ClampedArray that can be used to view the initial pixel data. Each pixel is represented by four 1bytes values (in the order of red, green, blue and transparency values; this is the "RGBA" format). Each color value part is represented by 0 to 255. Each part is assigned a consecutive index within the array, with the red part of the upper left pixel at index 0 of the array. Pixels are processed from left to right, then down, traversing the entire array

The size of the canvas I use here is 1000 * 1000, and the coordinate system is 1000 for the x-axis and 1000 for the y-axis

In fact, four similar numbers of RGBA (255,255,255,0) represent one pixel. Then 1000 1000 is represented by a Uint8ClampedArray array. How many elements are there in total? is 1000 1000 * 4 elements

The first step is to use the measureText method to calculate the appropriate size and position of the text.

 const canvas = document.createElement('canvas')
 const ctx = canvas.getContext('2d');
 canvas.setAttribute('width', 1000);
 canvas.setAttribute('height', 1000);
 ctx.clearRect(0, 0, 1000, 1000)
 ctx.font = 'bold 10p Arial'
 const measure = ctx.measureText(this.text)
 const size = 0.15
 const fSize = Math.min(1000 * size * 10 / 7, 1000 * size * 10 / measure.width);  
 // 10像素字体行高 lineHeight=7 magic
  ctx.font = `${fSize}px Arial`;
  let left = (1000 - measure.width) / 2 - measure.width / 2;
  const bottom = (1000 + fSize / 10 * 7) / 2;
  ctx.fillText(this.text, left, bottom)
  const imageData = ctx.getImageData(0, 0, 1000, 1000).data
  for (let x = 0; x < 1000; x += 4) {
      for (let y = 0; y < 1000; y += 4) {
          const fontIndex = (x + y * 1000) * 4 + 3;
          if (imageData[fontIndex] > 0) {
              this.points.push({
                  x, y
              })
          }
      }
  }

In fact, here we get pixels, and get the pixels composed of text. Then we can render, here we look at the following video:

飞书20220309-111315.gif

Why use Motion Factory? ?

You don't need to rewrite any of these, you only need to use our animation factory and simple configuration to generate the above particle animation.

Configure on the page
截屏2022-03-09 上午11.34.13.png

You can configure on this page, configure the properties corresponding to the current action, and then this thing is related to our dsl, you must pay attention to the naming of this property. I will do a step-by-step analysis in a later article.

Analyze animation

The text particle animation in the picture above is 4 keyframes from the animation point of view, and then switches at the next step. That actually corresponds to the timeline of our animation factory.

The animation parameters corresponding to my first key frame, and the duration; then the first frame ends, and the second rhythm is played.

Then the corresponding views are actually 4 views, because views and actions have a one-to-one correspondence, or a one-to-many relationship.

That is, the root node has 4 views under our root as shown in the figure:

Then we select any view, click the + sign to select the particle bullet box as follows:

Select the particle, then the bottom progress bar will appear, click, you can edit the animation properties.

We set the parameters of the text and the size of the text particles.

As shown in the figure:

In fact, the latter few are the same operations. The only difference is that the displayed text is different. Another is to delay the execution of the action. The second delay is 1000 seconds, the third delay is 2000, and so on.

When you look at the progress bar like this, you will understand, at a glance

Then click the play button to preview the animation:

飞书20220309-111318.gif

HOW TO USE

This is actually very simple, click the download button in the lower left corner and a ts file will be generated

This ts file includes various information about the current view and resource view, but you don't need to consider it.

Install our sdk directly

yarn install @wind/core 

then call

import { play } from '@wind/core' 
play(animation(), {container: canvas 节点}) 

It's ready to use!

finally

If you have any questions, please share in the comments section!

Text/Fly

Pay attention to Dewu Technology and be the most fashionable technical person!


得物技术
851 声望1.5k 粉丝