motivation

Recently, I reviewed the Matrix and found that this digital rain effect is very cool. I also saw similar codes on the Internet before. I first thought about a way to achieve this, and finally referred to an idea given on the Internet, and finally wrote a VUE plug-in on NPM


Your own implementation method (failure)

For this kind of complex animation effects, Canvas is the first choice, and CSS can definitely do it, but it’s super complicated and has a lot of code. When I first saw this special effect, I thought: (1) Canvas is generally used to draw static images. Since this example is an animation effect, setTimeout or setInterval or RAF must be called. Raf is used here to continuously draw images to achieve dynamic effects, and RAF should be used. It has an advantage over setTimeout/setInterval in that it is an API provided by the browser for animation, which optimizes method calls at runtime and pauses animation if the page is not active. Effectively save CPU overhead (2) draw a black background (3) because the digital rain looks like an independent sequence of downward movement of numeric letters, so I need to create a new DigitRain class, set the digital rain in a variety of attributes, to control the digital rain movement characteristics, the code is shown below

    // Digital rain class (parameter is configuration object)
    function DigitRain(configObj){
        // Digital rain location (X-axis)
        this.digitRainXPos = configObj.digitRainXPos,
        // The location of the digital rain (Y-axis)
        this.digitRainYPos = configObj.digitRainYPos,
        // The falling speed of the digital rain
        this.rainVelocity = configObj.rainVelocity,
        // The numeric rain color
        this.rainColor = configObj.rainColor,
        // The trailing length of the numeric rain
        this.rainTailLength = configObj.rainTailLength,
        // Digital rain text content
        this.rainText = configObj.rainText,
        ...
    }
Copy the code

(4) Then write a draw method to control its movement, and finally call fillText in the canvas to draw the text. Finally, I found it too difficult to write for a while, especially the processing of the text trailing effect was troublesome and could not achieve the effect, so I gave up

Another way to think about it

(1) Raf is also used to achieve animation effect. First, the number of raindrops falling columns (width/font size) is calculated according to canvas width and font size. Use a rainDropArray to record the position of each column’s text on the Y-axis, starting with 0. The core data structure is in the rainDropArray(2) requestAnimationFrame parameter function. Use the for loop to iterate over the rainDropArray, and then use fillText to draw text to the canvas. The X-axis position is the array index* font size, and the Y-axis position is the rainDropArray[I] value. And each fillText uses the encapsulated random method to get the random alphanumeric character of the string. Very clever here, for the tail effect, need only in the parameter function of requestAnimationFrame fillRect (0, 0,. The canvas. Width, canvas, height). The fillStyle is set to rgba(0,0,0,alpha), so that each drawing will have a black background, which will cover the previous drawing letters and make the letters lighter, so that the trailing effect can be achieved, and the trailing length can be controlled by controlling the value of alpha. Note that the drawing does not use clearReact to clear the last drawing. Each time, it overlays the last drawing

requestAnimationFrame
requestAnimationFrame
rainDropArray

 if(textYPostion>this.canvasHeight){
       if(Math.random()>0.9) {this.rainDropPositionArray[i]=0; }}Copy the code

Vue plug-in wrapped code

The total amount of code is small, less than 150 lines, the template part is a canvas

<template>
    <canvas id="vue-matrix-raindrop"></canvas>
</template>

<script>
    export default {
    name: 'vue-matrix-raindrop', // Plugins props:{// Canvas width canvasWidth:{type:Number, default:800}, // canvasHeight:{type:Number, default:600}, // drop fontSize:{type:Number, default:20}, // fontFamily:{type:String,
            default:'arial'}, // the textContent of the font will be randomly selected from the string textContent:{type:String,
            default:'abcdefghijklmnopqrstuvwxyz'}, // textColor:{type:String,
            default:'#0F0',
            validator:function(value){
                 var colorReg = /^#([0-9a-fA-F]{6})|([0-9a-fA-F]{3})$/g
                 returnColorreg. test(value)}}, //canvas backgroundColor can be customized backgroundColor:{type:String,
            default:'rgba (0,0,0,0.1)',
            validator:function(value){ var reg = /^[rR][gG][Bb][Aa][\(]((2[0-4][0-9]|25[0-5]|[01]? [0-9] [0-9]?) ,) {2} (2 [0-4] [0-9] 25 [0 to 5) | | [01]? [0-9] [0-9]?) ,? (0 \ \ d {1, 2} | 1 | 0)? [\] {1} $/;returnreg.test(value); }}, // Speed :{type:Number,
            default:2,
            validator:function(value){
              return value%1 === 0;
            }
        }
    },
    mounted:function(){ this.initRAF(); this.initCanvas(); this.initRainDrop(); this.animationUpdate(); }, methods:{// initialize requestAnitaionFrame for compatibilityinitRAF(){
             window.requestAnimationFrame = (function() {return window.requestAnimationFrame       ||
                      window.webkitRequestAnimationFrame ||
                      window.mozRequestAnimationFrame    ||
                      window.oRequestAnimationFrame      ||
                      function( callback ){ window.setTimeout(callback, 1000 / 60); }; }) (); window.cancelAnimationFrame = (function () {
               return window.cancelAnimationFrame ||
                      window.webkitCancelAnimationFrame ||
                      window.mozCancelAnimationFrame ||
                      window.oCancelAnimationFrame ||
                      function(id) { window.clearTimeout(id); }; }) (); }, // initialize canvasinitCanvas(){
             this.canvas = document.getElementById('vue-matrix-raindrop'); // We need to determine whether the canvas acquired is a real canvasif(this.canvas.tagName.toLowerCase() ! = ='canvas'){
                console.error("Error! Invalid canvas! Please check the canvas's id!")
             }
             this.canvas.width = this.canvasWidth;
             this.canvas.height = this.canvasHeight;
             this.canvasCtx = this.canvas.getContext('2d');
             this.canvasCtx.font = this.fontSize+'px '+this.fontFamily; this.columns = this.canvas.width / this.fontSize; }, // Initializes the initial y position of the digital rain fallinitRainDrop() {for(var i=0; i<this.columns; i++){ this.rainDropPositionArray.push(0); }}, // core animation function, control digital rain fallanimationUpdate(){this.speedcnt ++; // The speed is 1. The larger the speed, the slower the speedif(this.speedCnt===this.speed){ this.speedCnt = 0; // Draw background this.canvasctx. fillStyle= this.backgroundcolor; Enclosing canvasCtx. FillRect (0, 0, this. Canvas. Width, enclosing canvas. Height); // Draw the text this.canvasctx. fillStyle=this.textColor; // Iterate over the numeric rain in each column and draw the numeric letter on the canvasfor(var i=0,len=this.rainDropPositionArray.length; i<len; i++){ this.rainDropPositionArray[i]++; var randomTextIndex = Math.floor(Math.random()*this.textContent.length); var randomText = this.textContent[randomTextIndex]; var textYPostion = this.rainDropPositionArray[i]*this.fontSize; this.canvasCtx.fillText(randomText,i*this.fontSize,textYPostion); // If the digital rain touches the bottom of the canvas, it will return to the top and continue fallingif(textYPostion>this.canvasHeight){
               if(Math. The random () > 0.9) {enclosing rainDropPositionArray [I] = 0; } } } } window.requestAnimationFrame(this.animationUpdate) } },data () {
    	return{ canvasCtx:null, canvas:null, columns:0, rainDropPositionArray:[], speedCnt:0 } } } </script> <! -- Add"scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
Copy the code

The Github address is here