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

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

捕获物体

多边形以及不规则图形的捕获非常复杂,采用的方法是分离轴定理(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;
        }
   }
}

捕获动态物体

<!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);

<!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>

抛掷物体

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

<!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>


加入边界检测

<!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>

加入重力和反弹消耗

<!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轴方向上的速度

水平/垂直缓动

<!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>

任意方向缓动

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

<!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>

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

<!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>

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

<!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>

常见应用:放大缩小

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

<!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>

常见应用:淡入淡出

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

<!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>


常见应用:颜色渐变

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

<!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;

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

无摩擦力的弹性动画

<!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>

有摩擦力的弹性动画

<!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>

弹性动画的鼠标追随

<!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>

无重力绳球运动

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

<!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>

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

<!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>

目录
相关文章
|
6月前
|
前端开发 JavaScript 开发者
【QML进阶 进度条设计】打造动态弧形进度条特效
【QML进阶 进度条设计】打造动态弧形进度条特效
379 2
|
2天前
|
前端开发
Filp动画
Filp动画基于First、Last、Inverse、Play四步实现复杂前端动画效果。首先记录元素初始位置,接着记录目标位置,然后使用transform属性将元素逆向平移回初始位置,最后通过移除transform并添加transition属性实现平滑动画效果。这是一种高效解决CSS不支持过渡动画问题的方法。
|
4月前
|
前端开发 JavaScript 流计算
canvas系列教程05 ——交互、动画
canvas系列教程05 ——交互、动画
24 0
|
11月前
|
图形学 iOS开发
Unity——动效与缓动动画
Unity——动效与缓动动画
147 0
|
移动开发 前端开发 JavaScript
【前端动画】实现动画的6种方式
【前端动画】实现动画的6种方式
1200 0
An动画基础之元件的图形动画与按钮动画
An动画基础之元件的图形动画与按钮动画
332 0
An动画基础之元件的图形动画与按钮动画
|
存储 文件存储 云计算
不瞒了,我们和追光动画有一个《杨戬》!
不瞒了,我们和追光动画有一个《杨戬》!
190 0
An动画优化之遮罩层动画
An动画优化之遮罩层动画
265 0
An动画优化之遮罩层动画
|
前端开发
动画
动画
139 0
|
前端开发 图形学 Python
动画系统之2D动画
动画系统之2D动画
181 0
动画系统之2D动画