1 /** 2 * Created by EricXie on 2019/6/19. 3 */ 4 export default class Vertex { 5 static BASE_Y = 150; 6 static BASE_R = 10; 7 static FRICTION = 0.1;//波形抖动后回复到正常状态的速率指数 8 static DECELERATION = 0.95; 9 static SPEED_OF_BASE_WAVE = 3; 10 theta = 0; 11 goalY = 0; 12 amp = 0; 13 x; 14 y; 15 16 constructor(prmID, parent) { 17 this.theta = 360 * prmID / ( parent.NUM - 1);//角度的弧度值。根据NUM值将舞台上分为NUM块,然后将2π的弧度分配给各块,这样舞台上平静的时候正好是一段完整的波形。 18 this.x = prmID * parent.STAGE_W / (parent.NUM - 1); 19 this.y = Vertex.BASE_Y + Vertex.BASE_R * Math.sin(this.theta * Math.PI / 180); 20 } 21 22 //让波形不断波动的函数,不断更新各点的y坐标 23 updatePos(diffVal) { 24 this.theta += Vertex.SPEED_OF_BASE_WAVE; 25 if (this.theta >= 360) { 26 this.theta -= 360; 27 } 28 this.goalY = Vertex.BASE_Y + Vertex.BASE_R * Math.sin(this.theta * Math.PI / 180); 29 this.goalY += diffVal; 30 this.amp += this.goalY - this.y; 31 this.y += this.amp * Vertex.FRICTION;//y坐标以FRICTION的缓冲速率缓冲到正常状态 32 this.amp *= Vertex.DECELERATION; 33 } 34 }
/** * Created by EricXie on 2019/6/19. */ import Vertex from "./Vertex.js"; export default class Wave{ STAGE_W=800; STAGE_H=300; NUM=800; MOUSE_DIFF_RATIO=1; AUTO_INTERVAL=3000; vertexes=[]; mdlPt=[]; diffPt=[[],[]]; startIndex=[0,0]; mouseOldY; mouseNewY; mouseDiff=0;//mouseDiffGoal的缓冲 mouseDiffGoal=0;//鼠标拖动后产生的位相差 autoTimer; autoDiff=0;//计时器自动生成的位相差 mouseY=0; mouseX=0; constructor(){ this.canvas=this.createCanvas(); this.ctx=this.canvas.getContext("2d"); this.init(); this.animation(); } createCanvas(){ if(this.canvas) return this.canvas; let canvas=document.createElement("canvas"); Object.assign(canvas.style,{ "800px", height:"300px", backgroundColor:"#FFFFFF", margin:"auto" }); canvas.addEventListener("mousemove",this.mouseHandler.bind(this)); return canvas; } mouseHandler(e){ this.mouseX=e.clientX; this.mouseY=e.clientY; } appendTo(parent){ parent.appendChild(this.canvas); } animation(){ requestAnimationFrame(this.animation.bind(this)); this.updateMouseDiff(); this.updateWave(); } init(){ for (let i=0; i<this.NUM; i++) { let vertex=new Vertex(i,this); this.vertexes.push(vertex); //中点作成 if (i>1) { this.mdlPt.push( {x:(this.vertexes[i-1].x+this.vertexes[i].x)*0.5,y:(this.vertexes[i-1].y+this.vertexes[i].y)*0.5}); } //差分 this.diffPt[0].push( 0 ); this.diffPt[1].push( 0 ); } this.mouseNewY=this.mouseY; if (this.mouseNewY<0) { this.mouseNewY=0; } else if (this.mouseNewY > this.STAGE_H) { this.mouseNewY=this.STAGE_H; } this.mouseOldY=this.mouseNewY; setInterval(this.generateAutoWave.bind(this),this.AUTO_INTERVAL); } updateMouseDiff(){ this.mouseOldY=this.mouseNewY; this.mouseNewY=this.mouseY; if (this.mouseNewY<0) { this.mouseNewY=0; } else if (this.mouseNewY > this.STAGE_H) { this.mouseNewY=this.STAGE_H; } this.mouseDiffGoal = (this.mouseNewY - this.mouseOldY) * this.MOUSE_DIFF_RATIO; } updateWave(){ this.ctx.clearRect(0,0,this.STAGE_W,this.STAGE_H); this.mouseDiff -= (this.mouseDiff - this.mouseDiffGoal)*0.3; this.autoDiff-=this.autoDiff*0.9;//波形自动波动时的速率 let mX=this.mouseX; if (mX<0) { mX=0; } else if (mX > this.STAGE_W-2) { mX=this.STAGE_W-2; } this.startIndex[0] = 1+Math.floor( (this.NUM-2) * mX / this.STAGE_W );//startIndex[0]表示波形图上,鼠标拖动的那个点,用Math.floor是 //可以取到NUM个点里面x坐标小于当前鼠标x坐标的最大值 this.diffPt[0][this.startIndex[0]] -= ( this.diffPt[0][this.startIndex[0]] - this.mouseDiff )*0.99; //自动波 this.diffPt[1][this.startIndex[1]] -= ( this.diffPt[1][this.startIndex[1]] - this.autoDiff )*0.99; let d; let i; for ( i=this.startIndex[0]-1; i >=0; i--) { d=this.startIndex[0]-i; if (d>15) { d=15; } this.diffPt[0][i] -= ( this.diffPt[0][i] - this.diffPt[0][i+1] )*(1-0.01*d); } for ( i=this.startIndex[0]+1; i < this.NUM; i++) { d=i-this.startIndex[0]; if (d>15) { d=15; } this.diffPt[0][i] -= ( this.diffPt[0][i] - this.diffPt[0][i-1] )*(1-0.01*d); } for ( i=this.startIndex[1]-1; i >=0; i--) { d=this.startIndex[1]-i; if (d>15) { d=15; } this.diffPt[1][i] -= ( this.diffPt[1][i] - this.diffPt[1][i+1] )*(1-0.01*d); } for ( i=this.startIndex[1]+1; i < this.NUM; i++) { d=i-this.startIndex[1]; if (d>15) { d=15; } this.diffPt[1][i] -= ( this.diffPt[1][i] - this.diffPt[1][i-1] )*(1-0.01*d); } for ( i=0; i < this.NUM; i++) { this.vertexes[i].updatePos( this.diffPt[0][i]+this.diffPt[1][i]);//更新波形上各点的位相,位相差等于鼠标抖动的和自动产生的,即为diffPt[0][i]+diffPt[1][i] } for ( i=0; i < this.NUM-2; i++) { this.mdlPt[i].y = (this.vertexes[i+1].y + this.vertexes[i+2].y)*0.5;//更新波形图上两点中点的位相,使波形图看起来更流畅 } this.drawWave(); } drawWave(){ this.ctx.fillStyle="#666666"; this.ctx.beginPath(); this.ctx.moveTo(this.STAGE_W, this.STAGE_H); this.ctx.lineTo(0, this.STAGE_H); this.ctx.lineTo(this.vertexes[0].x, this.vertexes[0].y-50); this.ctx.quadraticCurveTo(this.vertexes[1].x, this.vertexes[1].y-50, this.mdlPt[0].x, this.mdlPt[0].y-50); for (let i=2; i<this.NUM-2; i++) { this.ctx.quadraticCurveTo(this.vertexes[i].x, this.vertexes[i].y-50, this.mdlPt[i-1].x, this.mdlPt[i-1].y-50); } this.ctx.quadraticCurveTo( this.vertexes[this.NUM-2].x, this.vertexes[this.NUM-2].y-50, this.vertexes[this.NUM-1].x, this.vertexes[this.NUM-1].y-50); this.ctx.closePath(); this.ctx.fill(); } generateAutoWave(){ this.autoDiff=200;//自动生成100的位相差 this.startIndex[1] = Math.round( Math.random()*(this.NUM-1) ); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script type="module"> import Wave from "./js/wave.js"; let wave=new Wave(); wave.appendTo(document.body); </script> </body> </html>