I. Knowledge preparation

1. The animation

Animation – our eyes has the characteristics of “persistence of vision”, when we see a picture or an object, is not going to disappear in 0.34 seconds, when we switch to draw in 0.34 seconds, there will be a smooth animation effects, the whole picture looks like moving, so when I was a child picture books, fast flip when you feel like the whole picture to move up, They are essentially static pictures that quickly switch between animations

Frame – the smallest unit of a single image in a video animation. A frame is a still picture. An animation is made up of several still faces

Frames Per Second (FPS) is the number of images that can be transmitted in one Second. It is also known as the number of times a GRAPHICS processor can be refreshed Per Second

Bullet screen effect – Our bullet screen effect is to slide every frame by constantly erasing the canvas and redrawing the canvas

2.requestAnimationFrame

RequestAnimationFrame () tells the browser that you want to perform an animation, and asks the browser to call the specified callback function to update the animation before the next redraw. This means that the number of redraws per second depends on the display refresh rate, which is usually 60 or 75Hz. This means that 60 or 75 times per second, we will pass in a function to continuously clean and draw the canvas

Compare requestAnimationFrame and setTimeout only from an animation perspective

advantages

1. RequestAnimationFrame follows the browser refresh rate, and there are no transitions (drawing too fast, exceeding the browser refresh rate, and losing frames), or delays caused by drawing too slow, and setTimeout and setInterva are not very accurate. Once the set time is not consistent with the browser refresh time, it is easy to transition drawing and lag

2. Animations are paused automatically if the page is not active, saving CPU (when we switch to another page while drawing the bullet, the bullet will be paused until you switch back to the page), and setTimeout will always run in the background

Disadvantages:

1. Compatibility is not as good as setTimeout and setInterva

3. The canvas

Initialize the canvas

This.context = this.canvas.getContext("2d") // Obtain the canvas object this.canvas = document.getelementByID (Canvas) // Obtain the width and height of the canvas container. Canvas is similar to bitmap. This.canvasheight = canvasHeight * 2 this.canvasWidth = canvasWidth * 2 // Set the canvas width and height this.canvas.width = this.canvasWidth this.canvas.height = this.canvasHeightCopy the code

Canvas draws text

This.context. font = '20px STheiti', this.context.font = '20px STheiti', SimHei '// Draw the text position this.context.fillText(draw the text, fill the starting abscissa (X axis), fill the starting ordinate (Y axis), fill the maximum width of the text) example :this.context.fillText(' bring you', 500, 400) we will achieve the sliding effect of the bullet screen by constantly modifying the X-axis coordinates, and can also control the speed of the bullet screenCopy the code

Canvas clear content

This.context. clearRect(x, y, width, height) this.context.clearRect(0, 0, 0) This.canvaswidth, this.canvasHeight) example :this.context.clearRect(0, 0, 500, 400) each frame erases the entire screen and redraws all bullet screensCopy the code

Canvas measures text width

The measureText() function allows us to measure the width of the text

   let width = this.context.measureText(value).width
Copy the code

What it does: We use text width to do two things: 1. Determine if the text is fully displayed on the screen. 2. Determine if the text has completely slid out of the screen

Ii. Barrage realization

In this example, we use Vue. We introduce the barrage. Js in barrage. We need to pass the barrageEffect.Barrage parameter (ID of canvas, width of canvas, height of Canvas, line count of Barrage, font size)

barrage.vue

<template> <div class="barrage"> <img src="https://images.pexels.com/photos/2286895/pexels-photo-2286895.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500" alt=""> <canvas id="myCanvas"> </canvas> </div> </template> <script> import barrageEffect from './barrage' export default { data  () { return { imgUrl:'', canvasWidth:0, canvasHeight:0, context:null, mobile:0, barrage:null, } }, methods: { init(){ const fontSize = 20 const highwayAmount = 4 this.barrage = new BarrageEffect. Barrage (' myCanvas, enclosing canvasWidth, enclosing canvasHeight, highwayAmount, fontSize) / / can add the Barrage by addTest method continuously Enclosing barrage. AddTest ([' good scenery beauty ', 'no no', 'want to go to', 'arrangements',' wake up wake up. You don't have the money to ']) setTimeout (() = > {this. Barrage. AddTest ([' young ', 'mouse tail,' consumption consumption reflection, 'this is your dream to do it', 'wake up wake up. Move the brick honestly ',' if you dream again, don't blame me to punch you '])},500)}, setCanvas(){ let canvasStyle = document.getElementById('myCanvas') this.canvasWidth = canvasStyle.offsetWidth this.canvasHeight =canvasStyle.offsetHeight this.init() } }, computed:{ }, mounted(){ this.setCanvas() } }; </script> <style lang="scss" scoped> .barrage{ width: 100vw; height: 100vh; position: relative; #myCanvas{ position: absolute; z-index: 2; width: 565px; height: 377px; top: 0; left: 0; } img{ width: 565px; height: 377px; } } </style>Copy the code

barrage.js

The class Barrage {constructor (canvas, canvasWidth canvasHeight, highwayAmount, fontSize) {/ / for this canvas object. Canvas = Document.getelementbyid (Canvas) document.getelementByID (canvas) document.getelementByID (canvas) document.getelementByID (canvas) document.getelementByID (canvas) document.context = this.canvas.getContext("2d") Generally, according to the multiples of the phone's screen, multiply the container width and height by 2 to match the double screen. This.canvasheight = canvasHeight * 2 this.canvasWidth = canvasWidth * 2 // Set the canvas width and height this.canvas.width = This.canvaswidth this.canvas.height = this.canvasHeight = this.canvasHeight = this.barragelist = []; This.fontsize = fontSize this.context.font = '${this.fontsize}px STheiti,' ${this.fontsize}px STheiti, SimHei 'this. highwayAmount = [] // Initialize the highwayAmount of the barrage, dividing the barrage into roads, to prevent the barrage from overlapping on the Y-axis, This.inittop (highwayAmount) // Whether drawing is complete this.draw = false} initTop(highwayAmount){// Maximum number of bullet lines const MaxHighwayAmount = parseInt(this.canvasHeight/(this.fontsize +20)) // If the number of rows passed is greater than the maximum, MaxHighwayAmount <highwayAmount? HighwayAmount = maxHighwayAmount: "//highwayAmount for(let I =1; i<=highwayAmount; {this.highwayamount.push (((this.fontsize +20)* I))}} drawBarrage(){// Empty the canvas this.context.clearrect (0, 0, 0); This.canvaswidth, this.canvasHeight) for(let I =0; let I =0; let I =0; i<this.barrageList.length; I++){// when the left(distance from the left of the canvas)+width <=0, the bullet is out of the screen. BarrageList [I].left + this.barragelist [I].width <=0? this.barrageList.splice(i,1) : ForEach ((val)=>{// Set the cursor color this.context.fillstyle = val.color // Draw the cursor position This.context.fillText(' ${val.value} ', val.left, val.top) When the left(distance from the left of canvas)+width of the bullet itself < the screen width, the bullet has been fully displayed in the screen, the occupied top can be released and a new value can be inserted. Val.occupation && val.left + val.width <= this.canvasWidth? Enclosing consumeText (val) : "' / / speed control barrage val. Left - = 2}) / / this. BarrageList is 0, that there is no barrage enclosing barrageList. Length = = 0? this.draw=false : RequestAnimationFrame (this.drawBarrage.bind(this))} requestAnimationFrame(this.drawBarrage.bind(this))} consumeText(val){// Set the cursor to false, Val.occupation && val.left + val.width <= this.canvasWidth? this.consumeText(val) : 'val. Occupation = false / / release the top value, to the enclosing highwayAmount return to occupy the top value, delay 0.5 s, SetTimeout (()=>{this.highwayamount.push (val.top) // Check if there are any pending projectiles. If there are, Insert a new value into this.barragelist if(this.textList.length! =0){this.barragelist.push (this.initTest(this.textList.splice(0,1)[0])}},500)} addTest(textList){// Check whether it is in the draw state If (this. The draw) {/ / judge whether there is a spare highwayAmount can use an if (this. HighwayAmount. Length! = 0){ let barrageList = textList.splice(0,this.highwayAmount.length) for(const val of barrageList){ this.barrageList.push(this.initTest(val)) } } this.textList.push(... textList) }else{ this.textList.push(... TextList) // Set the draw status to true this.draw = true this.runBarrage()}} initTest(text){// Initialize the barrage object let value = text let color = [' # E0FFFF ', '# FFE1FF', '# FFB5C5', '# F0FFF0', '# BFEFFF', '# 63 b8ff', '# FFFFFF'] let barrageTest = {value, / / barrage Top: this. HighwayAmount. Splice (0, 1) [0]. Left :this.canvasWidth,// Color :color[math.floor (math.random ()*6)], left: math.ceil (math.random ()* 3),// Bullet velocity Width: math.ceil (this.context.measuretext (value).width),// Get the occupation width of the damselact :true,// Use top status} return barrageTest} RunBarrage () {/ / according to the barrage of top several initialization data show for the first time this. The text list. The splice (0, this. HighwayAmount. Length). The forEach ((val) = > { This.barragelist.push (this.inittest (val))}) // Start drawing this.drawBarrage()}} export default{Barrage} this.barragelist.push (this.inittest (val))})Copy the code

Iii. Class function analysis and bullet screen overlap optimization

1. InitTop (highwayAmount) function (control Y axis bullet does not overlap)

Accept a parameter (highwayAmount), which represents the number of generated bullet screen lines, and control that bullet screen does not overlap on the Y axis. The Y axis of canvas is regarded as a road, and the width coordinates of each road are allocated according to the font size. Finally, according to the highwayAmount parameter, the Y axis coordinates of the road with the corresponding number of non-overlapping are obtained

The maximum number of bullet lines can be obtained by the height/text size of the canvas. To keep the distance between the lines, add 20px parseInt(this.canvasheight /(this.fontsize +20)).

Canvas draws the text X, and the y coordinate is calculated according to the lower left corner of the text. If the height is 0, the following situation will occur

So when we store the highway coordinates we need to get rid of the 0’s, so I starts at 1

for(let i =1; i<=highwayAmount; i++){ this.highwayAmount.push(((this.fontSize+20)*i)) }Copy the code

2. InitTest (text) functions

Information generated by barrage object storage barrage top: enclosing highwayAmount. Splice (0, 1) [0], / / return value of a top, and the top value from this. HighwayAmount, why want to do that, assuming we have 1 line barrage, When the first bullet screen is not fully displayed, the line cannot output the bullet screen. Otherwise, the bullet screen will overlap on the X-axis.

Example: As shown in the picture belowSo we remove the highwayAmount value from this. HighwayAmount to prevent it from being assigned to another barrage object. How can we tell if the barrage is fully displayed and can be added to other barrage objects

Occupation :true,// Occupies the top status. DrawBarrage () is required

3. DrawBarrage () function

Get the number of barrage rows from the this.highwayAmount array, initialize the first batch of displayed data, and call the this.drawBarrage() function to start drawing

4. AddTest (the text list) functions

It acts as the entry for drawing the barrage to determine whether the barrage is in the draw state according to this.draw. If the barrage is not in the draw state, add all data to this. TextList. If the barrage is in the draw state, check if there is any extra top to use. If there is, add the data directly to the this.barragelist and draw. Other data is added to this. TextList to be drawn

5. DrawBarrage () function

Each frame performs functions to control the movement of the barrage, add and delete the barrage data

ClearRect (0, 0, this.canvasWidth, this.canvasHeight) clears the canvas to redraw the bullet and its position

2. Clear the drawn bullet screen according to the left distance of the bullet screen from the canvas and its width3. Draw the bullet screen according to each bullet screen object, and judge whether it has been fully displayed in the canvas according to the left distance of the bullet screen from the canvas and its own widthIf so, call the consumeText(val) function to release the top held by the bullet object into this.highwayAmount and see if this. TextList is waiting to send a bullet. If so, add it to this.barragelist

3. The concluding

First published article, in the article the wrong place is many correct

Results show