十二、動畫 Animation
12.1清除畫布內容 Clear Canvas
爲了清除HTML5畫布,可以使用clearRect()方法來清除畫布位圖。該方法的性能比清除畫布的其他技術要好得多,比如重置畫布的寬度和高度,或者銷燬畫布元素然後重新創建它。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#buttons {
position: absolute;
top: 5px;
left: 10px;
}
#buttons > input {
padding: 10px;
display: block;
margin-top: 5px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<div id="buttons">
<input type="button" id="clear" value="Clear">
</div>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// begin custom shape
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.bezierCurveTo(430, 40, 370, 30, 340, 50);
context.bezierCurveTo(320, 5, 250, 20, 250, 50);
context.bezierCurveTo(200, 5, 150, 20, 170, 80);
// complete custom shape
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'blue';
context.stroke();
// bind event handler to clear button
document.getElementById('clear').addEventListener('click', function() {
context.clearRect(0, 0, canvas.width, canvas.height);
}, false);
</script>
</body>
</html>
代碼說明:點擊清除按鈕清除畫布。
以上代碼演示了點擊清除按鈕,清除畫布上的所有內容。
12.2動畫幀 Animation Frame
要使用HTML5 Canvas創建動畫,可以使用requestAnimFrame shim,它使瀏覽器能夠確定動畫的最佳FPS。對於每個動畫幀,我們可以更新畫布上的元素,清除畫布,重新繪製畫布,然後繼續請求另一個動畫幀。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRectangle(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y,
myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(myRectangle, canvas, context, startTime) {
// update
var time = (new Date()).getTime() - startTime;
var linearSpeed = 100;
// pixels / second
var newX = linearSpeed * time / 1000;
if(newX < canvas.width-myRectangle.width-myRectangle.borderWidth/2) {
myRectangle.x = newX;
}
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
drawRectangle(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(myRectangle, canvas, context, startTime);
});
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 0,
y: 75,
width: 100,
height: 50,
borderWidth: 5
};
drawRectangle(myRectangle, context);
// wait one second before starting animation
setTimeout(function() {
var startTime = (new Date()).getTime();
animate(myRectangle, canvas, context, startTime);
}, 1000);
</script>
</body>
</html>
以上代碼演示了在畫布上繪製一個矩形,並使矩形向右移動的動畫。注意代碼中的animate方法,該方法不斷更新矩形的位置,並清除畫布,將新的矩形繪製出來以實現矩形的移動。
12.3線性運動 Linear Motion
爲了用HTML5畫布創建線性運動動畫,我們可以根據速度方程增加每個幀對象的x、y值以改變對象的位置,計算方式爲:距離=速度*時間(distance = velocity * time)。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRectangle(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(myRectangle, canvas, context, startTime) {
// update
var time = (new Date()).getTime() - startTime;
var linearSpeed = 100;
// pixels / second
var newX = linearSpeed * time / 1000;
if(newX < canvas.width-myRectangle.width-myRectangle.borderWidth/2) {
myRectangle.x = newX;
}
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
drawRectangle(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(myRectangle, canvas, context, startTime);
});
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 0,
y: 75,
width: 100,
height: 50,
borderWidth: 5
};
drawRectangle(myRectangle, context);
// wait one second before starting animation
setTimeout(function() {
var startTime = (new Date()).getTime();
animate(myRectangle, canvas, context, startTime);
}, 1000);
</script>
</body>
</html>
這段代碼的演示內容與上一段基本一致,不在贅述。
12.4加速度 Acceleration
爲了用HTML5畫布創建二次運動動畫,我們可以爲每幀增加對象的vx(水平速度)、vy(垂直速度)或vx和vy兩者同時增加,然後根據加速度方程更新對象的位置,計算方法爲:距離=速度*時間+ 1/2 *加速度*時間^2(distance = velocity * time + 1/2 * acceleration * time^2)。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRectangle(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(myRectangle, canvas, context, startTime) {
// update
var time = (new Date()).getTime() - startTime;
// pixels / second^2
var gravity = 200;
myRectangle.y = 0.5 * gravity * Math.pow(time / 1000, 2);
if(myRectangle.y > canvas.height -
myRectangle.height - myRectangle.borderWidth / 2) {
myRectangle.y = canvas.height -
myRectangle.height - myRectangle.borderWidth / 2;
}
lastTime = time;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw
drawRectangle(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(myRectangle, canvas, context, startTime);
});
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 239,
y: 0,
width: 100,
height: 50,
borderWidth: 5
};
drawRectangle(myRectangle, context);
// wait one second before dropping rectangle
setTimeout(function() {
var startTime = (new Date()).getTime();
animate(myRectangle, canvas, context, startTime);
}, 2000);
</script>
</body>
</html>
以上代碼演示了在畫布上創建一個垂直降落的矩形,並且在降落過程中有一定的加速度,請注意代碼中與加速度有關的幾項常量,可以試着將其改爲更符合實際的重力加速度。
12.5振盪(往復)動畫 Oscillation Animation
爲了使用HTML5畫布創建振盪往復動畫,我們可以使用簡單的諧波振盪方程來設置圖形在每個幀的位置:
x(t) = 幅值*sin(t * 2PI/週期) + x0,(x(t) = amplitude * sin(t * 2PI / period) + x0)。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRectangle(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y,
myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(myRectangle, canvas, context, startTime) {
// update
var time = (new Date()).getTime() - startTime;
var amplitude = 150;
// in ms
var period = 2000;
var centerX = canvas.width / 2 - myRectangle.width / 2;
var nextX = amplitude * Math.sin(time * 2 * Math.PI / period) +
centerX;
myRectangle.x = nextX;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw
drawRectangle(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(myRectangle, canvas, context, startTime);
});
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 250,
y: 70,
width: 100,
height: 50,
borderWidth: 5
};
drawRectangle(myRectangle, context);
// wait one second before starting animation
setTimeout(function() {
var startTime = (new Date()).getTime();
animate(myRectangle, canvas, context, startTime);
}, 1000);
</script>
</body>
</html>
以上代碼演示了在畫布上繪製一個矩形,並使矩形進行振盪往復運動。
12.6動畫的啓動與停止 Start and Stop an Animation
要啓動HTML5畫布動畫,可以不斷地請求新的動畫幀;要停止HTML5畫布動畫,則停止請求新的動畫幀。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function drawRect(myRectangle, context) {
context.beginPath();
context.rect(myRectangle.x, myRectangle.y,
myRectangle.width, myRectangle.height);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = 'black';
context.stroke();
}
function animate(lastTime, myRectangle, runAnimation, canvas, context) {
if(runAnimation.value) {
// update
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
// pixels / second
var linearSpeed = 100;
var linearDistEachFrame = linearSpeed * timeDiff / 1000;
var currentX = myRectangle.x;
if(currentX < canvas.width -
myRectangle.width - myRectangle.borderWidth / 2) {
var newX = currentX + linearDistEachFrame;
myRectangle.x = newX;
}
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw
drawRect(myRectangle, context);
// request new frame
requestAnimFrame(function() {
animate(time, myRectangle, runAnimation, canvas, context);
});
}
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var myRectangle = {
x: 0,
y: 75,
width: 100,
height: 50,
borderWidth: 5
};
/*
* define the runAnimation boolean as an obect
* so that it can be modified by reference
*/
var runAnimation = {
value: false
};
// add click listener to canvas
document.getElementById('myCanvas')
.addEventListener('click', function() {
// flip flag
runAnimation.value = !runAnimation.value;
if(runAnimation.value) {
var date = new Date();
var time = date.getTime();
animate(time, myRectangle, runAnimation, canvas, context);
}
});
drawRect(myRectangle, context);
</script>
</body>
</html>
說明:點擊畫布啓動動畫並停止動畫。
以上代碼演示了通過點擊鼠標啓動和停止動畫,而不是像以上的代碼範例停止1秒後啓動動畫。