Canvas 動畫初體驗

星冉發表於2018-06-04

之前有介紹過canvas相關的基礎知識,今天來分享一個有關canvas的小動畫,先來說一下我們的基本思路,整體分為兩步,首先畫出背景,接下來開始實現動態效果,至於最終的結果是怎麼樣的,往下看就知道了

第一章  我是canvas你還記得我嗎?

自從上次與canvas約會過後,總覺得他是一個有潛力的小傢伙,只不過他的才能好像被埋沒了,如果還能在見到他,我想給他個驚喜

說來也很巧,居然和他偶遇了……

<div id="wrap">
   <canvas id="canvas" width="800" height="600"></canvas>		
</div>
複製程式碼

我,當然還記得你啊……

第二章  猜猜我的揹包裡都有什麼?

揹包這麼鼓,你都帶什麼啦?你猜呀……

  • 建立基本的頁面--- html
  • 存放圖片的檔案 -- image
  • 寫邏輯的頁面  ---- js檔案
  • 相關的圖片檔案,程式碼已上傳到github上,地址在下下下下面,歡迎訪問!

第三章   帶你去個地方

  • 背景搭建
   我們在對應的js(main.js)檔案中,引入背景圖片

var can,ctx,w,h
var img=new Image()
var star=new Image()
function init(){
	can=document.getElementById('canvas')
	ctx=can.getContext('2d')
	w=can.width
	h=can.height
	img.src="image/girl.jpg"
	star.src="image/star.png"
	gameloop()
}

function gameloop(){
/* 做相容, 迴圈呼叫gameloop,這裡的requestAnimationFrame 方法封裝在commonFunctions.js中*/ 
       window.requestAnimationFrame(gameloop)
	drawImage()
}
document.body.onload=init
// 繪製圖片
function drawImage(){
	ctx.drawImage(img,0,0,w,h)
}
複製程式碼

迴圈呼叫 gameloop的三種方法:                             
 requestAnimFrame(function(){})  根據效能確定時間,間隔時間不固定
 setTimeout(function(){},time)   幾秒後第一次呼叫
 setInterval(function(){},time)  第一次呼叫幾秒後再一次呼叫複製程式碼

這樣我們的背景就畫好了,像這樣

Canvas 動畫初體驗

  • 來點特效
    先畫一個小星星,這裡我們新建一個js檔案(stars.js)

  

// 首先定義一個star的類
var starObj=function(){
	this.x;
	this.y;
}
// 先來繪製一個看看效果
function drawStar(){
     ctx.drawImage(star,300,400)
}
// 把上面的drarStar() 方法在之前的main.js呼叫,後面在stars.js中方法都在main.js中呼叫  複製程式碼

function gameloop(){
	window.requestAnimationFrame(gameloop)/* 做相容, 迴圈呼叫gameloop*/
	drawImage()
	drawStar()	
}
複製程式碼

然後我們看效果,出現了一排小星星,那按照剛才的邏輯,我們要展示出很多的小星星

Canvas 動畫初體驗

在定義好的main.js中,定義要展示星星的數量,

// 數量
var num=100
var starts=[]  // 存放星星的陣列
function init(){
	can=document.getElementById('canvas')
	ctx=can.getContext('2d')
	w=can.width
	h=can.height
	img.src="image/girl.jpg"
	star.src="image/star.png"
	// 初始化星星
	for(var i=0;i<num;i++){
	  starts[i]=new starObj()
	  starts[i].init()
	}
	gameloop()
}
複製程式碼

同樣的在stars.js中也初始化一下

starObj.prototype.init=function(){
// 給星星一個隨機位置
	this.x=Math.random()*800
	this.y=Math.random()*600
}
starObj.prototype.draw=function(){
	/*context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
	 使用的圖片,開始剪下的x位置,開始剪下的y的位置,被剪下的圖片的寬,被剪下的圖片的高,
         在畫布上的x位置,在畫布上的y的位置,要使用的影象的寬,要使用的影象的高*/
	ctx.drawImage(star,this.x,this.y)
}
// 繪製一個
function drawStar(){
	for(var i=0;i<num;i++){
		starts[i].draw()
	}
}複製程式碼
我們現在繪製了100個,來看看現在的效果

Canvas 動畫初體驗

距離目標已經實現了一大步了,接下來我們來隨機擷取要顯示的星星,就像下面這樣

Canvas 動畫初體驗

要實現上面的效果,其實很簡單隻需這樣(starts.js)

var starObj=function(){
	this.x;
	this.y;
	this.picnum; // 定義一個隨機數
}
starObj.prototype.init=function(){
	this.x=Math.random()*800
	this.y=Math.random()*600
	//隨機位置
	this.picnum=Math.floor(Math.random()*7)	
}
starObj.prototype.draw=function(){
	/*context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
	 使用的圖片,開始剪下的x位置,開始剪下的y的位置,被剪下的圖片的寬,被剪下的圖片的高,
         在畫布上的x位置,在畫布上的y的位置,要使用的影象的寬,要使用的影象的高*/
	//ctx.drawImage(star,this.x,this.y)
	ctx.save()
	ctx.drawImage(star,this.picnum*7,0,7,7,this.x,this.y,7,7)
	ctx.restore()
}複製程式碼
在上面的程式碼中我們使用到了之前說過的save() , restore() 如果不太記得了,可以稍稍的去複習一下,然後我們要做的就是讓星星閃起來,由於我們 使用的requestAnimationFrame 時間間隔不固定,這裡我們可以利用這個時間差來實現星星的閃動效果

// main中定義上一幀的時間,和事件差
var lastTime
var deltaTime
function init(){
	can=document.getElementById('canvas')
	ctx=can.getContext('2d')
	w=can.width
	h=can.height
	img.src="image/girl.jpg"
	star.src="image/star.png"
	// 初始化星星
	for(var i=0;i<num;i++){
		//push
		starts[i]=new starObj()
		starts[i].init()
	}
      // 初始化上一幀的時間為當前時間
	lastTime=Date.now()    
	gameloop()
}
function gameloop(){
	window.requestAnimationFrame(gameloop)
	var now=Date.now()  // 當前時間
	deltaTime=now-lastTime  // 時間差
	lastTime=now  // 上一幀等於當前時間
	drawImage()
	drawStar()	
}
複製程式碼

// 在stars中實現閃動
var starObj=function(){
	this.x;
	this.y;
	this.picnum
	this.timer // 定義一個時間值
}
starObj.prototype.init=function(){
	this.x=Math.random()*800
	this.y=Math.random()*600
	this.picnum=Math.floor(Math.random()*7)
	this.timer=0  // 初始化為0
}
starObj.prototype.update=function(){
	this.timer+=deltaTime
        // 當時間值大於50毫秒後,動畫幀加1,且不超過7個
	if(this.timer>50){
		this.picnum+=1
		this.picnum%=7
		this.timer=0
	}
	// 這裡由於星星圖片只有7個動畫幀效果
	if(this.picnum>=7){
		this.picnum=0
	}
}複製程式碼
這樣小星星就隨機的閃起來了有沒有,怎麼樣這個地方還是很美的是不是,耐心的等一下

第四章  我會魔法哦

揮一揮魔法棒,讓小星星動起來

// 依舊是在stars中操作
var starObj=function(){
	this.x;
	this.y;
	this.picnum
	this.timer
       // 定義個初速度
	this.xSpd
	this.ySpd
}
starObj.prototype.init=function(){
	this.x=Math.random()*800
	this.y=Math.random()*600
	this.picnum=Math.floor(Math.random()*7)
	this.timer=0
	// 位移,來個隨機的上下左右
	this.xSpd=Math.random()*3-1.5 // [-1.5,1.5]
	this.ySpd=Math.random()*3-1.5
}
starObj.prototype.update=function(){
	// 星星移動
	this.x+=this.xSpd*deltaTime*0.002
	this.y+=this.ySpd*deltaTime*0.002
	// 超出邊界的星星重生
	if(this.x<0||this.x>800){
		this.init()
		return
	}
	if(this.y<0||this.y>600){
		this.init()
		return
	}
	this.timer+=deltaTime
	if(this.timer>50){
		this.picnum+=1
		this.picnum%=7
		this.timer=0
	}
	if(this.picnum>=7){
		this.picnum=0
	}
}
function drawStar(){
    for(var i=0;i<num;i++){
	starts[i].draw()
	starts[i].update()  // 別忘了這裡
   }
}
複製程式碼

是不是很神奇,別急,我還有一個魔法,來個滑鼠滑過顯示

// mian中定義如下
var switchy=false
var alive=0  // 星星的顯示狀態,初始化為0
var px,py
function init(){
	can=document.getElementById('canvas')
	ctx=can.getContext('2d')
	w=can.width
	h=can.height
	// 初始化滑鼠滑過事件
	document.addEventListener('mousemove',mousemove,false)
	img.src="image/girl.jpg"
	star.src="image/star.png"
	for(var i=0;i<num;i++){
		//push
		starts[i]=new starObj()
		starts[i].init()
	}
	lastTime=Date.now()
        
	gameloop()
}
function mousemove(e){
  if(e.offsetX||e.layerX){
	px=e.offsetX==undefined?e.layerX:e.offsetX
	py=e.offsetY==undefined?e.layerY:e.offsetY
	// 判斷滑鼠是否在畫布內
	if(px>0 && px<800 && py>0 && py<600){
	  switchy=true
	}else{
	  switchy=false
	}		
  }
}複製程式碼
stars中的更新邏輯

starObj.prototype.draw=function(){
  ctx.save()
// 這裡使用使用全域性透明度,它會控制整個畫布的透明度,也就是為什麼要把這部分內容寫在save和restore中
  ctx.globalAlpha=alive
  ctx.drawImage(star,this.picnum*7,0,7,7,this.x,this.y,7,7)
  ctx.restore()
}
// aliveUpdate 方法不要忘了在main中呼叫哦
function aliveUpdate(){
	if(switchy){
		alive+=0.03*deltaTime*0.05
		if(alive>1){
			alive=1
		}
	}else{
		alive-=0.03*deltaTime*0.05
		if(alive<0){
			alive=0
		}
	}
}複製程式碼

這樣當滑鼠滑過的時候就會看到神奇的效果了,
這裡要感謝Daisy老師的課程講解,下面是github的地址

    https://github.com/aurora-polaris/canvas-2