书籍名称:HTML5-Animation-with-JavaScript
书籍源码:https://github.com/lamberta/html5-animation1
1.释放
现在假设我们写一个动画,将它运动到指定的地方,先设置一个速度,运用三角函数,我们计算x的速度,计算y的速度,判断距离。到达终点时停止。
这种方法在有些情况适用,但某些情况下,我们想让物体运动的自然一些。
在一些运动,我们知道目标,且一开始速度很快,后面逐渐变慢。也就是说,它的速度和距离是成正比的。看下图。
01-easing-1.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Easing 1</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), ball = new Ball(), easing = 0.05, targetX = canvas.width / 2, targetY = canvas.height / 2; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var vx = (targetX - ball.x) * easing, vy = (targetY - ball.y) * easing; ball.x += vx; ball.y += vy; ball.draw(context); }()); }; </script> </body> </html>
增加拖拽
02-easing-2.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Easing 2</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Press and drag circle with mouse.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball = new Ball(), easing = 0.05, targetX = canvas.width / 2, targetY = canvas.height / 2, isMouseDown = false; canvas.addEventListener('mousedown', function () { if (utils.containsPoint(ball.getBounds(), mouse.x, mouse.y)) { isMouseDown = true; canvas.addEventListener('mouseup', onMouseUp, false); canvas.addEventListener('mousemove', onMouseMove, false); } }, false); function onMouseUp () { isMouseDown = false; canvas.removeEventListener('mouseup', onMouseUp, false); canvas.removeEventListener('mousemove', onMouseMove, false); } function onMouseMove () { ball.x = mouse.x; ball.y = mouse.y; } (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); if (!isMouseDown) { var vx = (targetX - ball.x) * easing, vy = (targetY - ball.y) * easing; ball.x += vx; ball.y += vy; } ball.draw(context); }()); }; </script> </body> </html>
按之前的想法速度是接近0的但不会为了,我们要加一个停止条件
03-easing-off.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Easing Off</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <textarea id="log"></textarea> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), log = document.getElementById('log'), ball = new Ball(), easing = 0.05, targetX = canvas.width / 2, animRequest; ball.y = canvas.height / 2; (function drawFrame () { animRequest = window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = targetX - ball.x; if (Math.abs(dx) < 1) { ball.x = targetX; /* ERRATA: 'cancelRequestAnimationFrame' renamed to 'cancelAnimationFrame' to reflect an update to the W3C Animation-Timing Spec. * See utils.js for the update to check for cross-browser compatibility. */ window.cancelAnimationFrame(animRequest); log.value = "Animation done!"; } else { var vx = dx * easing; ball.x += vx; } ball.draw(context); }()); }; </script> </body> </html>
移动到鼠标,是先获取鼠标的位置,并作为运动的目标点。
04-ease-to-mouse.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Ease to Mouse</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball = new Ball(), easing = 0.05; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var vx = (mouse.x - ball.x) * easing, vy = (mouse.y - ball.y) * easing; ball.x += vx; ball.y += vy; ball.draw(context); }()); }; </script> </body> </html>
补充一下,释放(easing)不只是使用与运动,它是一种由快变慢的效果,还适用于旋转(如大转盘),颜色等。
2.弹性
一维弹簧效果
弹簧效果离中心点的距离和加速度成正比。实际的加速度应该是距离*系数。
05-spring-1.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Spring 1</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), ball = new Ball(), spring = 0.03, targetX = canvas.width / 2, vx = 0; ball.y = canvas.height / 2; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = targetX - ball.x, ax = dx * spring; vx += ax; ball.x += vx; ball.draw(context); }()); }; </script> </body> </html>
比较真实的弹簧效果
在实际生活有摩擦力或是阻力,物体不会无休止的运动,下面将阻力的考虑在内。
06-spring-2.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Spring 2</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), ball = new Ball(), spring = 0.03, friction = 0.95, targetX = canvas.width / 2, vx = 0; ball.y = canvas.height / 2; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = targetX - ball.x, ax = dx * spring; vx += ax; vx *= friction; ball.x += vx; ball.draw(context); }()); }; </script> </body> </html>
这里只是简单的用阻力来控制速度无限接近0,需要补充一个到目标停止计算的条件类似如下
if (Math.abs(vx) > 0.001) {
vx += ax;
vx *= friction;
ball.x += vx;
}
二维考虑y方向
07-spring-3.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Spring 3</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), ball = new Ball(), spring = 0.03, friction = 0.95, targetX = canvas.width / 2, targetY = canvas.height / 2, vx = 0, vy = 0; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = targetX - ball.x, dy = targetY - ball.y, ax = dx * spring, ay = dy * spring; vx += ax; vy += ay; vx *= friction; vy *= friction; ball.x += vx; ball.y += vy; ball.draw(context); }()); }; </script> </body> </html>
在目标移动的情况
考虑移动到鼠标位置的弹簧效果,将目标设置为鼠标位置即可。
08-spring-4.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Spring 4</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball = new Ball(), spring = 0.03, friction = 0.95, vx = 0, vy = 0; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = mouse.x - ball.x, dy = mouse.y - ball.y, ax = dx * spring, ay = dy * spring; vx += ax; vy += ay; vx *= friction; vy *= friction; ball.x += vx; ball.y += vy; ball.draw(context); }()); }; </script> </body> </html>
考虑弹簧线
给弹簧效果加条弹簧线,让其更真实一点。
用api画一条圆心到鼠标位置的线就可以了。
考虑重力时,只需再加上重力加速度。
09-spring-5.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Spring 5</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball = new Ball(), spring = 0.03, friction = 0.9, gravity = 2, vx = 0, vy = 0; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = mouse.x - ball.x, dy = mouse.y - ball.y, ax = dx * spring, ay = dy * spring; vx += ax; vy += ay; vy += gravity; vx *= friction; vy *= friction; ball.x += vx; ball.y += vy; context.beginPath(); context.moveTo(ball.x, ball.y); context.lineTo(mouse.x, mouse.y); context.stroke(); ball.draw(context); }()); }; </script> </body> </html>
链式效果
链式效果的是对单个效果的发散,第一个球的目标为鼠标,第二个球的目标为第一个球的中心,依次类推。
10-chain.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Chain</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball0 = new Ball(), ball1 = new Ball(), ball2 = new Ball(), spring = 0.03, friction = 0.9, gravity = 2; function move (ball, targetX, targetY) { ball.vx += (targetX - ball.x) * spring; ball.vy += (targetY - ball.y) * spring; ball.vy += gravity; ball.vx *= friction; ball.vy *= friction; ball.x += ball.vx; ball.y += ball.vy; } (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); move(ball0, mouse.x, mouse.y); move(ball1, ball0.x, ball0.y); move(ball2, ball1.x, ball1.y); //draw spring context.beginPath(); context.moveTo(mouse.x, mouse.y); context.lineTo(ball0.x, ball0.y); context.lineTo(ball1.x, ball1.y); context.lineTo(ball2.x, ball2.y); context.stroke(); //draw balls ball0.draw(context); ball1.draw(context); ball2.draw(context); }()); }; </script> </body> </html>
用数组管理
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Chain Array</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), balls = [], numBalls = 10, spring = 0.03, friction = 0.9, gravity = 2; while (numBalls--) { balls.push(new Ball(20)); } function move (ball, targetX, targetY) { ball.vx += (targetX - ball.x) * spring; ball.vy += (targetY - ball.y) * spring; ball.vy += gravity; ball.vx *= friction; ball.vy *= friction; ball.x += ball.vx; ball.y += ball.vy; } function draw (ballB, i) { //if first ball, move to mouse if (i === 0) { move(ballB, mouse.x, mouse.y); context.moveTo(mouse.x, mouse.y); } else { var ballA = balls[i-1]; move(ballB, ballA.x, ballA.y); context.moveTo(ballA.x, ballA.y); } context.lineTo(ballB.x, ballB.y); context.stroke(); ballB.draw(context); } (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); context.beginPath(); balls.forEach(draw); }()); }; </script> </body> </html>
多个弹簧
12-multi-spring.html
12-multi-spring.html
与目标保持一定距离
利用三角函数,根据鼠标位置计算出将要运动到的目标。之后同上。
13-offset-spring.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Offset Spring</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Move mouse on canvas element.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball = new Ball(), spring = 0.03, friction = 0.9, springLength = 100, vx = 0, vy = 0; (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); var dx = ball.x - mouse.x, dy = ball.y - mouse.y, angle = Math.atan2(dy, dx), targetX = mouse.x + Math.cos(angle) * springLength, targetY = mouse.y + Math.sin(angle) * springLength; vx += (targetX - ball.x) * spring; vy += (targetY - ball.y) * spring; vx *= friction; vy *= friction; ball.x += vx; ball.y += vy; context.beginPath(); context.moveTo(ball.x, ball.y); context.lineTo(mouse.x, mouse.y); context.stroke(); ball.draw(context); }()); }; </script> </body> </html>
双重弹簧
综合运用:用到保持距离,一般弹簧效果,移动目标等
14-double-spring.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Double Spring</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Press and drag circles with mouse.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball0 = new Ball(20), ball1 = new Ball(20), ball0_dragging = false, ball1_dragging = false, spring = 0.03, friction = 0.9, springLength = 100, vx = 0, vy = 0; ball0.x = Math.random() * canvas.width; ball0.y = Math.random() * canvas.height; ball1.x = Math.random() * canvas.width; ball1.y = Math.random() * canvas.height; canvas.addEventListener('mousedown', function () { if (utils.containsPoint(ball0.getBounds(), mouse.x, mouse.y)) { ball0_dragging = true; } if (utils.containsPoint(ball1.getBounds(), mouse.x, mouse.y)) { ball1_dragging = true; } }, false); canvas.addEventListener('mouseup', function () { if (ball0_dragging || ball1_dragging) { ball0_dragging = false; ball1_dragging = false; } }, false); canvas.addEventListener('mousemove', function () { if (ball0_dragging) { ball0.x = mouse.x; ball0.y = mouse.y; } if (ball1_dragging) { ball1.x = mouse.x; ball1.y = mouse.y; } }, false); function springTo (ballA, ballB) { var dx = ballB.x - ballA.x, dy = ballB.y - ballA.y, angle = Math.atan2(dy, dx), targetX = ballB.x - Math.cos(angle) * springLength, targetY = ballB.y - Math.sin(angle) * springLength; ballA.vx += (targetX - ballA.x) * spring; ballA.vy += (targetY - ballA.y) * spring; ballA.vx *= friction; ballA.vy *= friction; ballA.x += ballA.vx; ballA.y += ballA.vy; } (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); if (!ball0_dragging) { springTo(ball0, ball1); } if (!ball1_dragging) { springTo(ball1, ball0); } context.beginPath(); context.moveTo(ball0.x, ball0.y); context.lineTo(ball1.x, ball1.y); context.stroke(); ball0.draw(context); ball1.draw(context); }()); }; </script> </body> </html>
三重
好复杂……
15-triple-spring.html
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Triple Spring</title> <link rel="stylesheet" href="../include/style.css"> </head> <body> <header> Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a> </header> <canvas id="canvas" width="400" height="400"></canvas> <aside>Press and drag circles with mouse.</aside> <script src="../include/utils.js"></script> <script src="./classes/ball.js"></script> <script> window.onload = function () { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), mouse = utils.captureMouse(canvas), ball0 = new Ball(20), ball1 = new Ball(20), ball2 = new Ball(20), ball0_dragging = false, ball1_dragging = false, ball2_dragging = false, spring = 0.03, friction = 0.9, springLength = 100, vx = 0, vy = 0; ball0.x = Math.random() * canvas.width; ball0.y = Math.random() * canvas.height; ball1.x = Math.random() * canvas.width; ball1.y = Math.random() * canvas.height; ball2.x = Math.random() * canvas.width; ball2.y = Math.random() * canvas.height; canvas.addEventListener('mousedown', function () { if (utils.containsPoint(ball0.getBounds(), mouse.x, mouse.y)) { ball0_dragging = true; } if (utils.containsPoint(ball1.getBounds(), mouse.x, mouse.y)) { ball1_dragging = true; } if (utils.containsPoint(ball2.getBounds(), mouse.x, mouse.y)) { ball2_dragging = true; } }, false); canvas.addEventListener('mouseup', function () { if (ball0_dragging || ball1_dragging || ball2_dragging) { ball0_dragging = false; ball1_dragging = false; ball2_dragging = false; } }, false); canvas.addEventListener('mousemove', function () { if (ball0_dragging) { ball0.x = mouse.x; ball0.y = mouse.y; } if (ball1_dragging) { ball1.x = mouse.x; ball1.y = mouse.y; } if (ball2_dragging) { ball2.x = mouse.x; ball2.y = mouse.y; } }, false); function springTo (ballA, ballB) { var dx = ballB.x - ballA.x, dy = ballB.y - ballA.y, angle = Math.atan2(dy, dx), targetX = ballB.x - Math.cos(angle) * springLength, targetY = ballB.y - Math.sin(angle) * springLength; ballA.vx += (targetX - ballA.x) * spring; ballA.vy += (targetY - ballA.y) * spring; ballA.vx *= friction; ballA.vy *= friction; ballA.x += ballA.vx; ballA.y += ballA.vy; } (function drawFrame () { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); if (!ball0_dragging) { springTo(ball0, ball1); springTo(ball0, ball2); } if (!ball1_dragging) { springTo(ball1, ball0); springTo(ball1, ball2); } if (!ball2_dragging) { springTo(ball2, ball0); springTo(ball2, ball1); } context.beginPath(); context.moveTo(ball0.x, ball0.y); context.lineTo(ball1.x, ball1.y); context.lineTo(ball2.x, ball2.y); context.lineTo(ball0.x, ball0.y); context.stroke(); ball0.draw(context); ball1.draw(context); ball2.draw(context); }()); }; </script> </body> </html>