捕获物体

多边形以及不规则图形的捕获非常复杂,采用的方法是分离轴定理(SAT)和最小平移向量(MTV)。这里不展开介绍,有兴趣的小伙伴可以自行搜索了解一下。下面来介绍一下矩形和圆的捕获。

矩形的捕获

如果鼠标点击坐标落在矩形上,则说明捕获了这个矩形;如果鼠标点击坐标没有落在矩形上,则说明没有捕获到这个矩形。

if (mouse.x > rect.x &&
mouse.x < rect.x + rect.width &&
mouse.y > rect.y &&
mouse.y < rect.y + rect.height) {
……
}

圆的捕获

判定鼠标与圆心之间的距离。如果距离小于圆的半径,说明鼠标落在了圆上面;如果距离大于或等于圆的半径,说明鼠标落在了圆的外面。

dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
distance = Math.sqrt(dx*dx + dy*dy);
if(distance < ball.radius){
……
}

捕获静止物体

js/ball.js 中添加 checkMouse(),专门用来检测是否捕获了小球

Ball.prototype = {
checkMouse:function(mouse)
{
var dx = mouse.x - this.x;
var dy = mouse.y - this.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.radius) {
return true;
} else {
return false;
}
}
}

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_html

捕获动态物体

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_02

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(0, cnv.height / 2, 20);
var mouse = tools.getMouse(cnv);
//isMouseDown用于标识鼠标是否按下的状态
var isMouseDown = false;
var vx = 3;

cnv.addEventListener("mousedown", function () {
//判断鼠标点击坐标是否位于小球上,如果是,则isMouseDown为true
if (ball.checkMouse(mouse)) {
isMouseDown = true;
alert("捕获成功");
}
}, false);

(function drawFrame() {
window.requestAnimationFrame(drawFrame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

//如果鼠标不是按下状态,则小球继续运动,否则就会停止
if (!isMouseDown) {
ball.x += vx;
}

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

使用一个变量isMouseDown来标识鼠标是否按下的状态。然后为Canvas添加一个mousedown事件,并且在事件中对按下鼠标的坐标进行判断。在动画循环中,如果鼠标不是按下状态,则小球继续运动,否则就会停止。

拖拽物体

(1)捕获物体:在鼠标按下(mousedown)时,判断鼠标坐标是否落在物体上面,如果落在,就添加两个事件:mousemove和moveup。
(2)移动物体:在鼠标移动(mousemove)中,更新物体坐标为鼠标坐标。
(3)松开物体:在鼠标松开(mouseup)时,移除mouseup事件(自身事件也得移除)和mousemove事件。

cnv.addEventListener("mousedown", function () {
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener("mouseup", onMouseUp, false);
}, false);

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_03

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(cnv.width / 2, cnv.height / 2, 20);
ball.fill(cxt);
var mouse = tools.getMouse(cnv);
var dx = 0, dy = 0;

cnv.addEventListener("mousedown", function () {
if (ball.checkMouse(mouse)) {
//dx为鼠标与球心的水平偏移量
dx = mouse.x - ball.x;
//dy为鼠标与球心的垂直偏移量
dy = mouse.y - ball.y;
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener("mouseup", onMouseUp, false);
}
}, false);
function onMouseMove() {
//更新小球坐标
ball.x = mouse.x - dx;
ball.y = mouse.y - dy;

//加入边界限制
//当小球碰到左边界时
if (ball.x < ball.radius) {
ball.x = ball.radius;
//当小球碰到右边界时
} else if (ball.x > cnv.width - ball.radius) {
ball.x = cnv.width - ball.radius;
}
//当小球碰到上边界时
if (ball.y < ball.radius) {
ball.y = ball.radius;
//当小球碰到下边界时
} else if (ball.y > cnv.height - ball.radius) {
ball.y = cnv.height - ball.radius;
}
}
function onMouseUp() {
document.removeEventListener("mouseup", onMouseUp, false);
document.removeEventListener("mousemove", onMouseMove, false);
}

(function drawFrame() {
window.requestAnimationFrame(drawFrame, cnv);
cxt.clearRect(0, 0, cnv.width, cnv.height);

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

抛掷物体

用鼠标选中一个物体,拖拽它向某个方向移动,松开鼠标后物体会沿着拖拽的方向继续前进。在抛掷物体时,必须在拖拽物体的过程中计算物体的速度向量,并且在释放物体时将这个速度向量赋给物体。

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_04

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(cnv.width / 2, cnv.height / 2, 20);
ball.fill(cxt);
var mouse = tools.getMouse(cnv);

var isMouseDown = false;
var dx = 0, dy = 0;
//oldX和oldY用于存储小球旧的坐标
var oldX, oldY;
//初始速度vx和vy都为0
var vx = 0, vy = 0;

//添加mousedown事件
cnv.addEventListener("mousedown", function () {
//判断鼠标点击是否落在小球上
if (ball.checkMouse(mouse)) {
//鼠标按下小球时,isMouseDown设置为true
isMouseDown = true;
//鼠标按下小球时,将当前鼠标位置赋值给oldX和oldY
oldX = ball.x;
oldY = ball.y;
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener("mouseup", onMouseUp, false);
}
}, false);
function onMouseMove() {
//鼠标移动时,更新小球坐标
ball.x = mouse.x - dx;
ball.y = mouse.y - dy;
}
function onMouseUp() {
//鼠标松开时,isMouseDown设置为false
isMouseDown = false;
document.removeEventListener("mouseup", onMouseUp, false);
document.removeEventListener("mousemove", onMouseMove, false);
}

(function drawFrame() {
window.requestAnimationFrame(drawFrame, cnv);
cxt.clearRect(0, 0, cnv.width, cnv.height);

if (isMouseDown) {
//如果isMouseDown为true,用当前小球的位置减去上一帧的坐标
vx = ball.x - oldX;
vy = ball.y - oldY;

//如果isMouseDown为true,更新oldX和oldY为当前小球中心坐标
oldX = ball.x;
oldY = ball.y;
} else {
//如果isMouseDown为false,小球沿着抛掷方向运动
ball.x += vx;
ball.y += vy;
}

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="300" height="200" style="border:1px solid silver;"></canvas>
</body>
</html>

加入边界检测

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_html_05

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height / 2, 20);
ball.fill(cxt);
var mouse = tools.getMouse(cnv);

var isMouseDown = false;
var dx = 0, dy = 0;
//oldX和oldY用于存储小球旧的坐标
var oldX, oldY;
//初始速度vx和vy都为0
var vx = 0, vy = 0;

//添加mousedown事件
cnv.addEventListener("mousedown", function () {
//判断鼠标点击是否落在小球上
if (ball.checkMouse(mouse)) {
//鼠标按下小球时,isMouseDown设置为true
isMouseDown = true;
//鼠标按下小球时,将当前鼠标位置赋值给oldX和oldY
oldX = ball.x;
oldY = ball.y;
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener("mouseup", onMouseUp, false);
}
}, false);
function onMouseMove() {
//鼠标移动时,更新小球坐标
ball.x = mouse.x - dx;
ball.y = mouse.y - dy;

//加入边界限制
//当小球碰到左边界时
if (ball.x < ball.radius) {
ball.x = ball.radius;
//当小球碰到右边界时
} else if (ball.x > cnv.width - ball.radius) {
ball.x = cnv.width - ball.radius;
}
//当小球碰到上边界时
if (ball.y < ball.radius) {
ball.y = ball.radius;
//当小球碰到下边界时
} else if (ball.y > cnv.height - ball.radius) {
ball.y = cnv.height - ball.radius;
}
}
function onMouseUp() {
//鼠标松开时,isMouseDown设置为false
isMouseDown = false;
document.removeEventListener("mouseup", onMouseUp, false);
document.removeEventListener("mousemove", onMouseMove, false);
}

(function drawFrame() {
window.requestAnimationFrame(drawFrame, cnv);
cxt.clearRect(0, 0, cnv.width, cnv.height);

if (isMouseDown) {
//如果isMouseDown为true,用当前小球的位置减去上一帧的坐标
vx = ball.x - oldX;
vy = ball.y - oldY;

//如果isMouseDown为true,更新oldX和oldY为当前小球中心坐标
oldX = ball.x;
oldY = ball.y;
} else {
//如果isMouseDown为false,小球沿着抛掷方向运动
ball.x += vx;
ball.y += vy;
//边界反弹
//碰到右边界
if (ball.x > cnv.width - ball.radius) {
ball.x = cnv.width - ball.radius;
vx = -vx;
//碰到左边界
} else if (ball.x < ball.radius) {
ball.x = ball.radius;
vx = -vx;
}
//碰到下边界
if (ball.y > cnv.height - ball.radius) {
ball.y = cnv.height - ball.radius;
vy = -vy;
//碰到上边界
} else if (ball.y < ball.radius) {
ball.y = ball.radius;
vy = -vy;
}
}

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="300" height="200" style="border:1px solid silver;"></canvas>
</body>
</html>

加入重力和反弹消耗

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_06

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height, 20);
ball.fill(cxt);
var mouse = tools.getMouse(cnv);

var isMouseDown = false;
var dx = 0, dy = 0;
//oldX和oldY用于存储小球旧的坐标
var oldX, oldY;
//初始速度vx和vy都为0
var vx = 0, vy = 0;
//加入重力和反弹消耗
var gravity = 1.5;
var bounce = -0.8;

cnv.addEventListener("mousedown", function () {
//判断鼠标点击是否落在小球上
if (ball.checkMouse(mouse)) {
//鼠标按下小球时,isMouseDown设置为true
isMouseDown = true;
//鼠标按下小球时,将当前鼠标位置赋值给oldX和oldY
oldX = ball.x;
oldY = ball.y;
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener("mouseup", onMouseUp, false);
}
}, false);
function onMouseMove() {
//鼠标移动时,更新小球坐标
ball.x = mouse.x - dx;
ball.y = mouse.y - dy;

//加入边界限制
//当小球碰到左边界时
if (ball.x < ball.radius) {
ball.x = ball.radius;
//当小球碰到右边界时
} else if (ball.x > cnv.width - ball.radius) {
ball.x = cnv.width - ball.radius;
}
//当小球碰到上边界时
if (ball.y < ball.radius) {
ball.y = ball.radius;
//当小球碰到下边界时
} else if (ball.y > cnv.height - ball.radius) {
ball.y = cnv.height - ball.radius;
}
}
function onMouseUp() {
//鼠标松开时,isMouseDown设置为false
isMouseDown = false;
document.removeEventListener("mouseup", onMouseUp, false);
document.removeEventListener("mousemove", onMouseMove, false);
}

(function drawFrame() {
window.requestAnimationFrame(drawFrame, cnv);
cxt.clearRect(0, 0, cnv.width, cnv.height);

if (isMouseDown) {
//如果isMouseDown为true,用当前小球的位置减去上一帧的坐标
vx = ball.x - oldX;
vy = ball.y - oldY;

//如果isMouseDown为true,更新oldX和oldY为当前小球中心坐标
oldX = ball.x;
oldY = ball.y;
} else {
//如果isMouseDown为false,小球沿着抛掷方向运动
vy += gravity;
ball.x += vx;
ball.y += vy;
//边界检测
//碰到右边界
if (ball.x > canvas.width - ball.radius) {
ball.x = canvas.width - ball.radius;
vx = vx * bounce;
//碰到左边界
} else if (ball.x < ball.radius) {
ball.x = ball.radius;
vx = vx * bounce;
}
//碰到下边界
if (ball.y > canvas.height - ball.radius) {
ball.y = canvas.height - ball.radius;
vy = vy * bounce;
//碰到上边界
} else if (ball.y < ball.radius) {
ball.y = ball.radius;
vy = vy * bounce;
}
}

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="300" height="200" style="border:1px solid silver;"></canvas>
</body>
</html>

缓动动画

带有一定缓冲效果的动画。在动画过程中,物体在某一段时间会“渐进加速”或“渐进减速”,从而让物体运动看起来更为自然而逼真,实现思路如下:

(1)定义一个0~1之间的缓动系数easing。
(2)计算出物体与终点之间的距离。
(3)计算出当前速度,其中当前速度=距离×缓动系数。
(4)计算新的位置,其中新的位置=当前位置+当前速度。
(5)重复执行第2~4步,直到物体达到目标。

每一帧中都将物体与终点之间的距离乘以缓动系数,从而求出当前速度。随着距离的不断减小,速度也就不断减小

在摩擦力动画中,每一帧中,当前速度等于上一帧速度乘以摩擦系数,其中速度是按照固定比例改变的;但是在缓动动画中,每一帧中,当前速度等于距离乘以缓动系数,其中速度并不是按照固定比例改变的。

实际开发中,更倾向于使用缓动动画。因为相对摩擦力动画来说,缓动动画更加自然而平滑。

缓动动画不仅可以用于物体的运动,还可以应用于物体的其他各种属性,包括大小、颜色、透明度以及旋转等。

缓动动画的核心要点:
(1)当前速度 =(最终值 - 当前值)×缓动系数。
(2)新的值 = 当前值 + 当前速度。

var targetX = 任意位置;
var targetY = 任意位置;
//动画循环
var vx = (targetX – object.x) * easing;
var vy = (targetY– object.y) * easing;
  • targetX和targetY分别为目标的横坐标和纵坐标
  • easing为缓动系数,取值0~1,当系数越接近于1时,小球移动得越快;当系数越接近于0时,小球移动得越慢。
  • vx和vy分别为物体在x轴方向和y轴方向上的速度

水平/垂直缓动

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_缓动_07

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(0, cnv.height / 2);
//定义终点的x轴坐标
var targetX = cnv.width * (3 / 4);
//定义缓动系数
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var vx = (targetX - ball.x) * easing;
ball.x += vx;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

任意方向缓动

分解为水平和垂直方向缓动即可。

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_html_08

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(0, 0);
//定义终点的x轴坐标和y轴坐标
var targetX = cnv.width * (3 / 4);
var targetY = cnv.height * (1 / 2);
//定义缓动系数
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var vx = (targetX - ball.x) * easing;
var vy = (targetY - ball.y) * easing;
ball.x += vx;
ball.y += vy;

ball.fill(cxt);
})();

}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

实战范例:小球缓动追随鼠标

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_html_09

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(cnv.width / 2, cnv.height / 2, 15, "#FF6699");
var mouse = tools.getMouse(cnv);
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var vx = (mouse.x - ball.x) * easing;
var vy = (mouse.y - ball.y) * easing;
ball.x += vx;
ball.y += vy;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

实战范例:多小球缓动追随鼠标

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_10

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var bigBall = new Ball(cnv.width / 2, cnv.height / 2, 15, "# FF6699");
var smallBall = new Ball(cnv.width / 2, cnv.height / 2, 12, "#66CCFF");
var mouse = tools.getMouse(cnv);
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

//第1个小球跟随鼠标移动
var vx1 = (mouse.x - bigBall.x) * easing;
var vy1 = (mouse.y - bigBall.y) * easing;
bigBall.x += vx1;
bigBall.y += vy1;
bigBall.fill(cxt);

//第2个小球跟随第1个小球移动
var vx2 = (bigBall.x - smallBall.x) * easing;
var vy2 = (bigBall.y - smallBall.y) * easing;
smallBall.x += vx2;
smallBall.y += vy2;
smallBall.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

常见应用:放大缩小

缓动动画作用于尺寸(此处为半径)的改变

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_sed_11

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height / 2);
var targetRadius = 36;
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var vRadius = (targetRadius - ball.radius) * easing;
ball.radius += vRadius;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

常见应用:淡入淡出

缓动动画作用于透明度的改变

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_12

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(cnv.width / 2, cnv.height / 2, 30, "rgba(255,102,153,1.0)");
var opacity = 1.0;
var targetOpacity = 0.0;
var easing = 0.05;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var v = (targetOpacity - opacity) * easing;
opacity += v;
ball.color = "rgba(255,102,153," + opacity + ")";

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

常见应用:颜色渐变

缓动动画作用于颜色的改变

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_缓动_13

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(cnv.width / 2, cnv.height / 2, 30);
ball.fill(cxt);
var easing = 0.02;

var red = 255;
var green = 0;
var blue = 0;
var targetRed = 10;
var targetGreen = 255;
var targetBlue = 55;


(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var vRed = (targetRed - red) * easing;
var vGreen = (targetGreen - green) * easing;
var vBlue = (targetBlue - blue) * easing;

red += vRed;
green += vGreen;
blue += vBlue;

var color = "rgba(" + parseInt(red) + "," + parseInt(green) + ", " + parseInt(blue) + "," + "1.0)";
ball.color = color;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

弹性动画

物体滑动到终点后还会来回反弹一会儿,直至停止。

弹性动画中,跟距离成正比的是“加速度”。物体离终点越远,加速度越大。刚刚开始,由于加速度的影响,速度会快速增大。当物体接近终点时,加速度变得很小,但是它还在加速。由于加速度的影响,物体会越过终点。然后随着距离的变大,反向加速度也随之变大,就会把物体拉回来。物体在终点附近来回反弹一会儿,最终在摩擦力的作用下停止。

ax = (targetX - object.x) * spring;
ay = (targetY - object.y) * spring;
vx += ax;
vy += ay;
vx *= friction;
vy *= friction;
object.x += vx;
object.y += vy;

弹性动画的语法与缓动动画的语法是非常相似的,只不过缓动动画操作的是“速度”,而弹性动画操作的是“加速度”。

无摩擦力的弹性动画

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_sed_14

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化数据
var ball = new Ball(0, cnv.height / 2);
var targetX = cnv.width / 2;
var spring = 0.02;
var vx = 0;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var ax = (targetX - ball.x) * spring;
vx += ax;
ball.x += vx;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

有摩擦力的弹性动画

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_sed_15

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(0, cnv.height / 2);
var targetX = cnv.width / 2;
var spring = 0.02;
var vx = 0;
var friction = 0.95;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var ax = (targetX - ball.x) * spring;
vx += ax;
vx *= friction;
ball.x += vx;

ball.fill(cxt);
})();

}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

弹性动画的鼠标追随

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas_16

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height / 2);
var mouse = tools.getMouse(cnv);

var targetX = cnv.width / 2;
var spring = 0.02;
var vx = 0;
var vy = 0;
var f = 0.95;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

var ax = (mouse.x - ball.x) * spring;
var ay = (mouse.y - ball.y) * spring;

vx += ax;
vy += ay;

vx *= f;
vy *= f;

ball.x += vx;
ball.y += vy;

ball.fill(cxt);
})();
}</script>
</head>
<body>
<canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

无重力绳球运动

在弹性动画的鼠标追随的基础上,加一条连线即可

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_html_17

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height / 2);
var mouse = tools.getMouse(cnv);

var targetX = cnv.width / 2;
var spring = 0.02;
var vx = 0;
var vy = 0;
var friction = 0.95;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

//加入弹性动画
var ax = (mouse.x - ball.x) * spring;
var ay = (mouse.y - ball.y) * spring;
vx += ax;
vy += ay;
vx *= friction;
vy *= friction;
ball.x += vx;
ball.y += vy;
ball.fill(cxt);

//将鼠标以及小球中心连接成一条直线
cxt.beginPath();
cxt.moveTo(ball.x, ball.y);
cxt.lineTo(mouse.x, mouse.y);
cxt.stroke();
cxt.closePath();
})();
}</script>
</head>
<body>
<canvas id="canvas" width="270" height="180" style="border:1px solid silver;"></canvas>
</body>
</html>

有重力绳球运动(类似悠悠球)

canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_缓动_18

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/tools.js"></script>
<script src="js/ball.js"></script>
<script type="text/javascript">function $$(id) {
return document.getElementById(id);
}
window.onload = function () {
var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

var ball = new Ball(cnv.width / 2, cnv.height / 2);
var mouse = tools.getMouse(cnv);

var targetX = cnv.width / 2;
var spring = 0.02;
var vx = 0;
var vy = 0;
var friction = 0.95;
//定义重力
var gravity = 1;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

//加入弹性动画
var ax = (mouse.x - ball.x) * spring;
var ay = (mouse.y - ball.y) * spring;
vx += ax;
vy += ay;
//加入重力影响
vy += gravity;
vx *= friction;
vy *= friction;
ball.x += vx;
ball.y += vy;
ball.fill(cxt);

//将鼠标以及小球中心连接成一条直线
cxt.beginPath();
cxt.moveTo(ball.x, ball.y);
cxt.lineTo(mouse.x, mouse.y);
cxt.stroke();
cxt.closePath();
})();
}</script>
</head>
<body>
<canvas id="canvas" width="270" height="180" style="border:1px solid silver;"></canvas>
</body>
</html>