Imitate a live compliment animation

Posted by cpetercarter on Sun, 10 May 2020 06:29:26 +0200

Preface

Reading How does the H5 live mad compliment animation work?(with full source code) This article feels like it's a good point to follow and animate.The implementation of css is easy to understand, but I have different ideas after reading the author's implementation of canvas, so I implemented it according to my own ideas.The results are as follows:

Ideas for implementation

The first thing I think about when I see this animation effect is to declare an array of small icon objects that you can do by drawing them in different places.So first we'll animate a small icon.

Animation process decomposition

To make an animation, we first break down the entire animation process. This animation contains the following:

  • enlarge
    There was an enlarged animation just after the picture appeared
  • y-axis translation
  • x-axis translation
    That is, the animation in which the icon swings left and right
  • transparency
    Transparency animation when icon disappears

Single icon animation

Let's start by simplifying the problem and animating a single icon.Here, for later operations on icon animation, I wrote an Ani class representing icon animation, which contains the location properties, size, image object and animation track related properties of the icon, as well as a drawing method to draw it.Define as follows:

        //Icon Animation Class
         class Ani{
             constructor(img,x,y,w,h){
                this.x=x;
                this.y=y;
                this.width=w;
                this.height=h;
                this.image=img;
                //Randomly generated sinusoidal wave amplitude
                this.step=getRandom(10,60);
                //The moving speed of the randomly generated y-axis
                this.spite=getRandom(2,6);
                //Oscillating frequency of sinusoidal function
                this.frequency=getRandom(50,100);
                //Small Icon Transparency
                this.op=0;
                //Random Curve Type
                Math.random()>=0.5?this.type=1:this.type=2;
                //y-axis offset as a parameter of sine function
                this.dy=0;
             }
             //Global context s are not used here to facilitate reuse
             draw(context){ 
                 //y-axis translation drawing
                 this.y-=this.spite;
                 //x-axis pendulum animation
                 this.dy+=this.spite;
                 let dx=0;
                 //Different wave directions
                 this.type==1?dx=Math.sin(this.dy/this.frequency):dx=Math.sin(-this.dy/this.frequency);
                 let x=this.x+dx*this.step;  
                 //Enlarge Animation
                 if(this.width<50){
                    this.width+=0.5;
                    this.height+=0.5;
                 }
                 context.drawImage(this.image,x,this.y,this.width,this.height);
                 //Transparency animation when disappearing
                 if(this.y<50){
                    this.op+=0.05
                    context.fillStyle = `rgba(255,255,255,${this.op})`;
                    context.fillRect(x,this.y,this.width,this.height);
                 }
             }
         }
Copy Code

To explain the above code a little, the frequency attribute in the constructor is used to control the swing frequency of the sine function. You can modify its value to see the effect. The getRandom() method is used to get the random numbers between intervals. The source code of the line surface is given.

The transparency of the icon is achieved by adding a transparent background color to the specified location. You have searched many websites and have not found a way to directly modify the transparency of the image object, so you use this method.

The Ani class draw() method implements each animation process by changing the y, x, width, height, op and other properties of the Ani object.Here we will focus on the implementation of the swing animation of the x-axis. The motion animation of the x-axis is achieved by means of sine function. js has ready-made Math.sin() function for us to use. The 0->1->0->-1 curve of the sine function perfectly matches the motion curve we need.Here we take the y-axis offset dy as a parameter of the sine function, generate the value DX between 0 and 1, and use dx*step to get the x-axis offset.

After the Ani class is finished, we still need a function to start the animation:

    const canvas = document.getElementById('thumsCanvas');
    let context = canvas.getContext('2d');
    let image=new Image("./img/star.png");
    let ani=new Ani(image,250/2-50/2,500-50,20,20);
       //renderer
    function render(){
        //context.clearRect(0,0,250,500); // clear canvas
        //Add transparent background color fill for trailing effect
        context.fillStyle = 'rgba(255,255,255,0.5)';
        context.fillRect(0,0,250,500);
        ani.draw(context);
        window.requestAnimationFrame(render)
    }    
    render();
Copy Code

Multiple icon animation

If you have animated a single icon, it is easy to animate multiple icons.We just need to create an array that holds multiple icon objects and traverse to draw them each time the render function executes.

Since we need icon objects for more than one image, we need to implement a loadImage function to cache the image to be loaded into an imageList array, which refers to the above article How does the H5 live mad compliment animation work?(with full source code) .The code is as follows:

        //Load Image
        function loadImage(){
            const images=[
                '../img/red.png',
                '../img/dog.png',
                '../img/cat.png',
                '../img/star.png',
                '../img/zan.png',
            ];
            const promiseList=[];
            images.forEach(element => {
                let p=new Promise((resolve)=>{
                    const img=new Image();
                    img.onload=resolve.bind(null,img)
                    img.src=element;
                })
                promiseList.push(p)
            });
            return new Promise(resolve=>{
                Promise.all(promiseList).then(imgs=>{                        
                        this.imageList=imgs;
                        resolve();
                })
            })
        }
Copy Code

Then we need to add a method to the icon object:

        //Add Icon Object to Array
        function tapAdd(){
            let image=this.imageList[this.getRandom(0,5)];
            let ani=new Ani(image,250/2-50/2,500-50,20,20)
            aniList.push(ani)
        }
Copy Code

Finally, we also need a rendering function:

       //renderer
        function render(){
            //context.clearRect(0,0,250,500); // clear canvas
            context.fillStyle = 'rgba(255,255,255,0.5)';
            context.fillRect(0,0,250,500);
            aniList.forEach((ani,index)=>{
                if(ani.y<-50){
                    ani=null;
                }else{
                    ani.draw(context);
                }
            })
            window.requestAnimationFrame(render)
        }        
        loadImage().then(()=>{
            console.log('Image Loading Completed')
            render();
            setInterval(tapAdd,100);
        });
Copy Code

The two sentences context.fillStyle ='rgba (255,255,255,0.5)'; context.fillRect (0,0,250,500); are used to achieve a tailing effect by adding a translucent fill color, which can be directly replaced by context.clearRect(0,0,250,500) if not needed;

Full Source:

 <!DOCTYPE html>
 <html>
     <head>
        <title>cavans Praise animation effect</title>
     </head>
     <body>
        <canvas onclick="tapAdd()" id="thumsCanvas" width="250" height="500" style="width:250px;height:500px;background-color: #f4f4f4;"></canvas>
     </body>
     <script>
         //Icon Animation Class
         class Ani{
             constructor(img,x,y,w,h){
                this.x=x;
                this.y=y;
                this.width=w;
                this.height=h;
                this.image=img;
                //Randomly generated sinusoidal wave amplitude
                this.step=getRandom(10,60);
                //The moving speed of the randomly generated y-axis
                this.spite=getRandom(2,6);
                this.frequency=getRandom(50,100);
                //Small Icon Transparency
                this.op=0;
                //Random Curve Type
                Math.random()>=0.5?this.type=1:this.type=2;
                this.dy=0;
             }
             draw(context){ 
                //y-axis animation effect
                 this.y-=this.spite;
                //x-axis animation effect 
                 this.dy+=this.spite;
                 let dx=0;
                 //Different wave directions
                 this.type==1?dx=Math.sin(this.dy/this.frequency):dx=Math.sin(-this.dy/this.frequency);
                 let x=this.x+dx*this.step;  
                 //Image Enlargement Animation
                 if(this.width<50){
                    this.width+=0.5;
                    this.height+=0.5;
                 }
                 context.drawImage(this.image,x,this.y,this.width,this.height);
                 //Image vanishing animation, transparency from 0-1
                 if(this.y<50){
                    this.op+=0.05
                    context.fillStyle = `rgba(255,255,255,${this.op})`;
                    context.fillRect(x,this.y,this.width,this.height);
                 }
             }
         }
        //Array of small icon animation objects
        let aniList=[];
        const canvas = document.getElementById('thumsCanvas');
        let imageList=[];
        let context = canvas.getContext('2d');
        //Load Image
        function loadImage(){
            const images=[
                '../img/red.png',
                '../img/dog.png',
                '../img/cat.png',
                '../img/start.png',
                '../img/zan1.png',
            ];
            const promiseList=[];
            images.forEach(element => {
                let p=new Promise((resolve)=>{
                    const img=new Image();
                    img.onload=resolve.bind(null,img)
                    img.src=element;
                })
                promiseList.push(p)
            });
            return new Promise(resolve=>{
                Promise.all(promiseList).then(imgs=>{                        
                        this.imageList=imgs;
                        resolve();
                })
            })
        }
        //Get Random Numbers
        function getRandom(min,max){
            return Math.floor(Math.random()*(max-min))+min
        }
        //Add Icon
        function tapAdd(){
            let image=this.imageList[this.getRandom(0,5)];
            let ani=new Ani(image,250/2-50/2,500-50,20,20)
            aniList.push(ani)
        }
        //renderer
        function render(){
            //context.clearRect(0,0,250,500); // clear canvas
            context.fillStyle = 'rgba(255,255,255,0.5)';
            context.fillRect(0,0,250,500);
            aniList.forEach((ani,index)=>{
                if(ani.y<-50){
                    ani=null;
                }else{
                    ani.draw(context);
                }
            })
            window.requestAnimationFrame(render)
        }        
        loadImage().then(()=>{
            console.log('Image Loading Completed')
            render();
            setInterval(tapAdd,100);
        });
     </script>
 </html>
Copy Code

Picture material

zan.png

cat.png dog.png heart.png star.png

Last

That's what I'm reading. How does the H5 live mad compliment animation work?(with full source code) After that, I realized a live point of praise effect according to my own ideas, and expressed my thanks to the author.

If there are good suggestions for modification, you are welcome to make them, and if you feel OK, you are welcome to give them a compliment.

Topics: Attribute