在本章中,我们将在 HTML5 canvas中创建动画。
动画是连续的图像,使人产生了运动的幻觉。 但是,动画不仅限于运动。 随时间改变对象的背景也被视为动画。
在 HTML5 canvas中创建动画的函数有以下三个:
setInterval(function, delay)setTimeut(function, delay)requestAnimationFrame(callback)
setInterval()函数每隔延迟毫秒重复执行一次传递的函数。 setTimeout()以毫秒为单位执行指定的功能。 为了创建动画,从执行的函数中调用setTimeout()。 requestAnimationFrame()函数允许浏览器在下一次重绘之前调用指定的函数来更新动画。 浏览器进行了一些优化。
沿曲线移动
在第一个动画中,对象沿曲线移动。
move_along_curve.html
<!DOCTYPE html><html><head><title>HTML5 canvas move along curve</title><style>canvas { border: 1px solid #bbbbbb }</style><script>var canvas;var ctx;var x = 20;var y = 80;const DELAY = 30;const RADIUS = 10;function init() {canvas = document.getElementById('myCanvas');ctx = canvas.getContext('2d');setInterval(move_ball, DELAY);}function draw() {ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle = "cadetblue";ctx.arc(x, y, RADIUS, 0, 2*Math.PI);ctx.fill();}function move_ball() {x += 1;if (x > canvas.width + RADIUS) {x = 0;}y = Math.sin(x/32)*30 + 80;draw();}</script></head><body onload="init();"><canvas id="myCanvas" width="350" height="150"></canvas></body></html>
该示例沿正弦曲线移动一个圆。 圆圈移过画布的末端后,它再次出现在左侧。
setInterval(move_ball, DELAY);
setInterval()函数使move_ball()函数每DELAY ms 调用一次。
function draw() {ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle = "cadetblue";ctx.arc(x, y, RADIUS, 0, 2*Math.PI);ctx.fill();}
draw()方法使用clearRect()方法清除画布,并绘制具有更新的 x 和 y 坐标的新圆。
function move_ball() {x += 1;if (x > canvas.width + RADIUS) {x = 0;}y = Math.sin(x/32)*30 + 80;draw();}
在move_ball()函数中,我们更新圆心的 x 和 y 坐标。 我们检查球是否已通过画布的右边缘,然后调用draw()方法重绘画布。
淡出
淡出是改变对象状态的动画。 这是一种过渡动画。
fading_out.html
<!DOCTYPE html><html><head><style>canvas {border: 1px solid #bbbbbb}</style><title>HTML5 canvas fading out</title><script>var canvas;var ctx;var alpha = 1;var rx = 20;var ry = 20;var rw = 120;var rh = 80;const DELAY = 20;function init() {canvas = document.getElementById('myCanvas');ctx = canvas.getContext('2d');canvas.addEventListener("click", onClicked);ctx.fillRect(rx, ry, rw, rh)}function onClicked(e) {var cx = e.x;var cy = e.y;if (cx >= rx && cx <= rx + rw &&cy >= ry && cy <= ry + rh) {fadeout();}}function fadeout() {if (alpha < 0) {canvas.removeEventListener("click", onClicked);ctx.globalAlpha = 1;ctx.fillStyle = 'white';ctx.fillRect(rx, ry, rw, rh);return;}ctx.clearRect(rx, ry, rw, rh);ctx.globalAlpha = alpha;ctx.fillRect(rx, ry, rw, rh)alpha -= 0.01;setTimeout(fadeout, DELAY);}</script></head><body onload="init();"><canvas id="myCanvas" width="350" height="250"></canvas></body></html>
有一个矩形对象。 当我们单击矩形时,它开始淡出。
canvas.addEventListener("click", onClicked);
通过addEventListener()方法将click监听器添加到画布。 再次单击鼠标后,将调用onClicked()函数。
ctx.fillRect(rx, ry, rw, rh)
最初,在画布上以默认的黑色填充绘制了一个矩形。
function onClicked(e) {var cx = e.x;var cy = e.y;if (cx >= rx && cx <= rx + rw &&cy >= ry && cy <= ry + rh) {fadeout();}}
在onClicked()函数内,我们可以计算出鼠标单击的 x 和 y 坐标。 我们将鼠标坐标与矩形的外部边界进行比较,如果鼠标坐标落在矩形的区域内,则将调用fadeout()方法。
if (alpha < 0) {canvas.removeEventListener("click", onClicked);ctx.globalAlpha = 1;ctx.fillStyle = 'white';ctx.fillRect(rx, ry, rw, rh);return;}
当矩形完全透明时,我们将移走监听器并以不透明的白色填充该区域。 return语句结束fadeout()函数的递归调用。
ctx.clearRect(rx, ry, rw, rh);ctx.globalAlpha = alpha;ctx.fillRect(rx, ry, rw, rh)
矩形的区域被清除并填充有更新的 alpha 状态。
alpha -= 0.01;
alpha值减小一小部分。
setTimeout(fadeout, DELAY);
在DELAY ms 之后,从其内部调用fadeout()方法。 这种做法称为递归。
泡泡
以下示例受到 Java 2D 演示示例的启发。
bubbles.html
<!DOCTYPE html><html><head><title>HTML5 canvas bubbles</title><style>canvas {border: 1px solid #bbb;background: #000;}</style><script>var cols = ["blue", "cadetblue", "green", "orange", "red", "yellow","gray", "white"];const NUMBER_OF_CIRCLES = 35;const DELAY = 30;var maxSize;var canvas;var ctx;var circles;function Circle(x, y, r, c) {this.x = x;this.y = y;this.r = r;this.c = c;}function init() {canvas = document.getElementById('myCanvas');ctx = canvas.getContext('2d');circles = new Array(NUMBER_OF_CIRCLES);initCircles();doStep();}function initCircles() {var w = canvas.width;var h = canvas.height;maxSize = w / 10;for (var i = 0; i < circles.length; i++) {var rc = getRandomCoordinates();var r = Math.floor(maxSize * Math.random());var c = cols[Math.floor(Math.random()*cols.length)]circles[i] = new Circle(rc[0], rc[1], r, c);}}function doStep() {for (var i = 0; i < circles.length; i++) {var c = circles[i];c.r += 1;if (c.r > maxSize) {var rc = getRandomCoordinates();c.x = rc[0];c.y = rc[1];c.r = 1;}}drawCircles();setTimeout(doStep, DELAY);}function getRandomCoordinates() {var w = canvas.width;var h = canvas.height;var x = Math.floor(Math.random() * (w - (maxSize / 2)));var y = Math.floor(Math.random() * (h - (maxSize / 2)));return [x, y];}function drawCircles() {ctx.clearRect(0, 0, canvas.width, canvas.height);for (var i = 0; i < circles.length; i++) {ctx.beginPath();ctx.lineWidth = 2.5;var c = circles[i];ctx.strokeStyle = c.c;ctx.arc(c.x, c.y, c.r, 0, 2*Math.PI);ctx.stroke();}}</script></head><body onload="init();"><canvas id="myCanvas" width="350" height="250"></canvas></body></html>
在该示例中,有越来越多的彩色气泡在屏幕上随机出现和消失。
var cols = ["blue", "cadetblue", "green", "orange", "red", "yellow","gray", "white"];
这些颜色用于绘制气泡。
function Circle(x, y, r, c) {this.x = x;this.y = y;this.r = r;this.c = c;}
这是Circle对象的构造器。 除了 x 和 y 坐标以及半径之外,它还包含颜色值的属性。
circles = new Array(NUMBER_OF_CIRCLES);
circles数组用于容纳圆形对象。
for (var i = 0; i < circles.length; i++) {var rc = getRandomCoordinates();var r = Math.floor(maxSize * Math.random());var c = cols[Math.floor(Math.random()*cols.length)]circles[i] = new Circle(rc[0], rc[1], r, c);}
circles数组用圆圈填充。 我们计算随机坐标,随机初始半径和随机颜色值。
function doStep() {
doStep()表示程序的动画周期。
for (var i = 0; i < circles.length; i++) {var c = circles[i];c.r += 1;if (c.r > maxSize) {var rc = getRandomCoordinates();c.x = rc[0];c.y = rc[1];c.r = 1;}}
我们遍历circles数组并增加每个圆的半径。 当圆达到最大大小时,它会随机重新定位并最小化。
setTimeout(doStep, DELAY);
setTimeout()方法用于创建动画。 您可能需要调整DELAY值以适合您的硬件。
function drawCircles() {ctx.clearRect(0, 0, canvas.width, canvas.height);for (var i = 0; i < circles.length; i++) {ctx.beginPath();ctx.lineWidth = 2.5;var c = circles[i];ctx.strokeStyle = c.c;ctx.arc(c.x, c.y, c.r, 0, 2*Math.PI);ctx.stroke();}}
drawCircles()函数清除画布并绘制数组中的所有圆圈。
星空
下面的示例创建一个星空动画。
starfield.html
<!DOCTYPE html><html><head><title>HTML5 canvas star field</title><script>var canvas_w;var canvas_h;var canvas;var ctx;var layer1;var layer2;var layer3;const DELAY = 20;const N_STARS = 60;const SPEED1 = 3;const SPEED2 = 2;const SPEED3 = 1;function init() {canvas = document.getElementById("myCanvas");ctx = canvas.getContext("2d");canvas_w = canvas.width;canvas_h = canvas.height;layer1 = new layer(N_STARS, SPEED1, "#ffffff");layer2 = new layer(N_STARS, SPEED2, "#dddddd");layer3 = new layer(N_STARS, SPEED3, "#999999");setTimeout("drawLayers()", DELAY);}function star() {this.x = Math.floor(Math.random()*canvas_w);this.y = Math.floor(Math.random()*canvas_h);this.move = function(speed) {this.y = this.y + speed;if (this.y > canvas_h) {this.y = 0;this.x = Math.floor(Math.random()*canvas_w);}}this.draw = function(col) {ctx.fillStyle = col;ctx.fillRect(this.x, this.y , 1, 1);}}function layer(n, sp, col) {this.n = n;this.sp = sp;this.col = col;this.stars = new Array(this.n);for (var i=0; i < this.n; i++) {this.stars[i] = new star();}this.moveLayer = function() {for (var i=0; i < this.n; i++) {this.stars[i].move(this.sp);}}this.drawLayer = function() {for (var i=0; i < this.n; i++) {this.stars[i].draw(this.col);}}}function drawLayers() {ctx.fillStyle = '#000000';ctx.fillRect(0, 0, canvas_w, canvas_h);layer1.moveLayer();layer2.moveLayer();layer3.moveLayer();layer1.drawLayer();layer2.drawLayer();layer3.drawLayer();setTimeout("drawLayers()", DELAY);}</script></head><body onload="init();"><canvas id="myCanvas" width="800" height="600"></canvas></body></html>
通过产生三个不同的图层来创建星空动画。 每层由具有不同速度和颜色阴影的星星(小点)组成。 前层的星星更亮,移动速度更快,背面的星星更暗,移动速度更慢。
layer1 = new layer(N_STARS, SPEED1, "#ffffff");layer2 = new layer(N_STARS, SPEED2, "#dddddd");layer3 = new layer(N_STARS, SPEED3, "#999999");
创建了三层星星。 它们具有不同的速度和颜色阴影。
function star() {this.x = Math.floor(Math.random()*canvas_w);this.y = Math.floor(Math.random()*canvas_h);...
创建星后,它会被赋予随机坐标。
this.move = function(speed) {this.y = this.y + speed;if (this.y > canvas_h) {this.y = 0;this.x = Math.floor(Math.random()*canvas_w);}}
move()方法移动星星; 它增加了它的 y 坐标。
this.draw = function(col) {ctx.fillStyle = col;ctx.fillRect(this.x, this.y , 1, 1);}
draw()方法在画布上绘制星星。 它使用fillRect()方法以给定的颜色绘制一个小矩形。
function layer(n, sp, col) {this.n = n;this.sp = sp;this.col = col;this.stars = new Array(this.n);...
一层是具有给定速度和颜色阴影的n星的集合。 星星存储在stars数组中。
for (var i=0; i < this.n; i++) {this.stars[i] = new star();}
创建图层后,stars数组将填充星形对象。
this.moveLayer = function() {for (var i=0; i < this.n; i++) {this.stars[i].move(this.sp);}}
moveLayer()方法遍历星星数组,并调用每个星星的move()方法。
this.drawLayer = function() {for (var i=0; i < this.n; i++) {this.stars[i].draw(this.col);}}
同样,drawLayer()方法调用每个星星的draw()方法。
function drawLayers() {ctx.fillStyle = '#000000';ctx.fillRect(0, 0, canvas_w, canvas_h);layer1.moveLayer();layer2.moveLayer();layer3.moveLayer();layer1.drawLayer();layer2.drawLayer();layer3.drawLayer();setTimeout("drawLayers()", DELAY);}
drawLayers()函数可移动每一层的星星并将其绘制在画布上。 它在DELAY ms 之后调用自己,从而创建动画。
在 HTML5 画布教程的这一章中,我们介绍了动画。
