先看下效果,然後再說如何實現,最終效果如下:
我們知道動畫其實是不同的圖片循環替換,造成視覺上圖片在動。
我們製作這個動畫的素材如下:
從右至左,就是一個人的行走的所有動作,擡腿,邁步,腳落下,另一隻腳邁步,然後循環如此。
我們將圖中的五個動作圖截出來,循環播放,效果就如下:
可以看出,這是一個原地踏步的動作,如果給圖片加上位移,就是行走的效果。
這裏講一個知識點:Css spirit。
我們剛剛已經講了,動畫是5個動作的合成,應該有5張圖片,現在我們將5張圖片放在一張圖片裏,需要時截取圖片的部分,就可以獲取所需實際圖片了。
這麼做的原因是:
在實際的web應用中,頁面上可能有上百張甚至好幾百張圖片,如果每張圖片都是單獨的,就需要建立幾百個http連接,每個連接的建立都需要耗費一定時間,幾個個連接的時間浪費是非常大的。我們可以把多張圖片合成到一張大的圖片中,然後通過CSS中的背景定位等技術,把背景定位到實際的圖片位置,就可以得到所需圖片了(canvas也可以利用這種思想)。這樣可以節省大量的連接建立時間。是前端優化的一部分。
當然,現在我們使用各種工具構建前端工程,打包時會將很多小圖標直接轉成BASE64編碼。
(例如,webpack, 使用url-loader插件,設置limit,打包時,將所有小於limit的圖片進行BASE64編碼)。
源碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>walk</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<canvas width="500" height="400" id="canvas" style="margin-top: 200px;"></canvas>
<img src="../asserts/walk.jpg" style="display: none;" id="walk">
<script>
//獲取圖片
let walkImg = document.getElementById('walk');
//獲取繪圖上下文
let ctx = document.getElementById('canvas').getContext('2d');
//這個參數是用來標識,現在改用哪個用作,初始值是4,圖片最有邊的動作,開始行走的動作。取值是4~0,循環
let index = 4;
//行走位置
let position = 0;
//行走的方向,true代表從左至右, false代表從右至左
let direct = true;
//定時器,確保圖片加載完成再繪製動畫
let timer = null;
//確保圖片加載完成再繪製到
if (!walkImg.complete) {
timer = setInterval(() => {
//兩張圖片加載完成
if (walkImg.complete) {
//清除定時器
clearInterval(timer);
timer = null;
//繪製
drawAll();
}
}, 200);
} else {
drawAll();
}
function drawAll() {
//清除原來的圖層
ctx.clearRect(0, 0, 500, 400);
//畫腳下的線,當做地面
addLawn();
//添加初始動作
addWalk();
//使用定時器,不斷重繪畫面,形成動畫
setInterval(() => {
ctx.clearRect(0, 0, 500, 400);
addLawn();
addWalk();
}, 150);
}
//繪製線段,當做地面
function addLawn() {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = 'green';
ctx.lineWidth = 3;
ctx.moveTo(0, 112);
ctx.lineTo(500, 112);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
//添加不同位置圖片
function addWalk() {
ctx.save();
/**
* 行走分爲兩個方向,從左至右和從右至左,我們分別說明
* 1、從右至左:圖片資源的5個動作就是從右至左方向走的,所以從右至左行走時,無需
* 額外處理圖片,只需將對應動作放在規定位置即可。
*
* 2、從左至右:這與圖片資源的工作方向相反,所以,提取動作後,需要翻轉(利用scale進行翻轉)。
* */
if (direct) {
//從左至右方向,scale(-1, 1)先翻轉畫布,然後利用translate修改座標系原點
ctx.scale(-1, 1);
ctx.translate(-position-55,0);
position += 6;
} else {
//從右至左方向,無需翻轉,只需修改座標系原點,然後將圖片直接從原點處放置即可
ctx.translate(position,0);
position -= 6;
}
//根據index不同,從圖片資源中提取不同的行走動作
switch(index) {
case 0:
ctx.drawImage(walkImg, 0, 0, 55, 110, 0, 0, 55, 110);
break;
case 1:
ctx.drawImage(walkImg, 75, 0, 55, 110, 0, 0, 55, 110);
break;
case 2:
ctx.drawImage(walkImg, 145, 0, 55, 110, 0, 0, 55, 110);
break;
case 3:
ctx.drawImage(walkImg, 220, 0, 55, 110, 0, 0, 55, 110);
break;
case 4:
ctx.drawImage(walkImg, 270, 0, 55, 110, 0, 0, 55, 110);
break;
}
//圖片位置,走到這返回
if (position > 440) {
direct = false;
}
if (position < 10) {
direct = true;
}
index -=1 ;
if (index < 0) {
index = 4;
}
ctx.restore();
}
</script>
</body>
</html>
有疑問歡迎留言給我,或發送郵件至[email protected],歡迎大家討論交流。