canvas系列教程05 ——交互、动画

简介: canvas系列教程05 ——交互、动画

交互

实时获取鼠标在Canvas中的坐标

//将tools定义为window对象的属性,该属性的值是一个对象
window.tools = {};
//获取鼠标位置
window.tools.getMouse = function (element) {
  //定义一个mouse的对象
  var mouse = { x: 0, y: 0 };
  //为传入的元素添加mousemove事件
  addEvent(element, "mousemove", function (e) {
      var x, y;
      //在IE中,event对象是作为window对象的一个属性存在
      var e = e || window.event;
      //获取鼠标当前位置,并作兼容处理
      //兼容Firefox、chrome、IE9及以上
      if (e.pageX || e.pageY) {
            x = e.pageX;
            y = e.pageY;
      }
      //兼容IE8及以下,以及混杂模式下的Chrome和Safari
      else {
            x = e.clientX + document.body.scrollLeft || document.documentElement.scrollLeft;
            y = e.clientY + document.body.scrollTop || document.documentElement.scrollTop;
      }
      //将当前的坐标值减去canvas元素的偏移位置,则x、y为鼠标在canvas中的相对坐标
      x -= element.offsetLeft;
      y -= element.offsetTop;

      mouse.x = x;
      mouse.y = y;
  })
  //返回值为mouse对象
  return mouse;
}
var txt = $$("txt");
var mouse = tools.getMouse(cnv);

cnv.addEventListener("mousemove", function () {
  txt.innerHTML = "鼠标当前坐标为:(" + mouse.x + "," + mouse.y + ")";
}, false);

通过键盘控制图形移动

封装【获取键盘控制方向】的函数,写在 js/tools.js 中

//获取键盘控制方向
window.tools.getKey = function () {
   var key = {};
   window.addEventListener("keydown", function (e) {
        if (e.keyCode == 38 || e.keyCode == 87) {
              key.direction = "up";
        } else if (e.keyCode == 39 || e.keyCode == 68) {
              key.direction = "right";
        } else if (e.keyCode == 40 || e.keyCode == 83) {
              key.direction = "down";
        } else if (e.keyCode == 37 || e.keyCode == 65) {
              key.direction = "left";
        } else {
              key.direction = "";
        }
   }, false);
   return key;
}

keyCode

按键

W(上)

87

S(下)

83

A(左)

65

D(右)

68

38

40

37

39

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

            //初始化一个圆形
            drawBall(cnv.width / 2, cnv.height / 2);
            //初始化变量
            var x = 100;
            var y = 75;
            //获取按键方向
            var key = tools.getKey();

            //添加鼠标按下事件
            window.addEventListener("keydown", function (e) {
                //清除整个Canvas,以便重绘新的圆形
                cxt.clearRect(0, 0, cnv.width, cnv.height);

                //根据key.direction的值,判断小球移动方向
                switch (key.direction) {
                    case "up":
                        y -= 2;
                        drawBall(x, y);
                        break;
                    case "down":
                        y += 2;
                        drawBall(x, y);
                        break;
                    case "left":
                        x -= 2;
                        drawBall(x, y);
                        break;
                    case "right":
                        x += 2;
                        drawBall(x, y);
                        break;
                        //default值
                    default:
                        drawBall(x, y);
                }
            }, false);

            //定义绘制小球的函数
            function drawBall(x, y) {
                cxt.beginPath();
                cxt.arc(x, y, 20, 0, 360 * Math.PI / 180, true);
                cxt.closePath();
                cxt.fillStyle = "#6699FF";
                cxt.fill();
            }
        }
    </script>
</head>
<body>
    <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

按键盘上的方向键控制图形移动

switch语句,一定要加入default值的表达式,不然如果按下的是其他按键,小球就会消失了(没有重绘)。

switch (key.direction) {
   case "up":
        ……
   case "down":
        ……
   case "left":
        ……
   case "right":
        ……
   default:
        ……
}

动画

原理:通过 requestAnimationFrame()方法,循环清除画布,并重绘动画后的新图形。

requestAnimationFrame() 与 setInterval() 功能类似,但 setInterval() 存在性能问题,且需手动设置循环间隔时间,而requestAnimationFrame() 无需手动设置,它会自动根据浏览器绘制的帧率进行调整。


重要提醒:

(1)对于需要不断改变的变量,一般在动画循环之前先定义。

(2)对于需要不断改变的变量,一般在动画循环中图形绘制之后才递增或递减。


开发技巧:复杂的动画效果从x轴和y轴两个方向来考虑,实现的思路会变得非常清晰。

封装【requestAnimationFrame()方法兼容代码】的函数,写在 js/tools.js 中

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

             //初始化变量,也就是初始化圆的x轴坐标为0
             var x = 0;
             //动画循环
             (function frame() {
                 window.requestAnimationFrame(frame);
                 //每次动画循环都先清空画布,再重绘新的图形
                 cxt.clearRect(0, 0, cnv.width, cnv.height);

                 //绘制圆
                 cxt.beginPath();
                 cxt.arc(x, 70, 20, 0, 360 * Math.PI / 180, true);
                 cxt.closePath();
                 cxt.fillStyle = "#6699FF";
                 cxt.fill();

                 //变量递增
                 x += 2;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

物理动画

物理动画,即模拟现实世界的一种动画效果。在物理动画中,物体会遵循牛顿运动定律,如射击游戏中打出去的炮弹会随着重力而降落。

追随鼠标旋转

原理:每次鼠标移动的时候,计算鼠标当前位置与箭头中心的夹角,然后把这个夹角作为箭头旋转的角度,重绘箭头即可。

封装【绘制箭头】的函数,写在 js/arrow.js 中

function Arrow(x,y,color,angle)
{
   //箭头中心x坐标,默认值为0
   this.x = x || 0;
   //箭头中心y坐标,默认值为0
   this.y = y || 0;
   //颜色,默认值为"#FF0099"
   this.color = color || "#FF0099";
   //旋转角度,默认值为0
   this.angle = angle || 0;
}
Arrow.prototype = {
   stroke: function (cxt) {
        cxt.save();
        cxt.translate(this.x, this.y);
        cxt.rotate(this.angle);
        cxt.strokeStyle = this.color;
        cxt.beginPath();
        cxt.moveTo(-20, -10);
        cxt.lineTo(0, -10);
        cxt.lineTo(0, -20);
        cxt.lineTo(20, 0);
        cxt.lineTo(0, 20);
        cxt.lineTo(0, 10);
        cxt.lineTo(-20, 10);
        cxt.closePath();
        cxt.stroke();
        cxt.restore();
   },
   fill: function (cxt) {
        cxt.save();
        cxt.translate(this.x, this.y);
        cxt.rotate(this.angle);
        cxt.fillStyle = this.color;
        cxt.beginPath();
        cxt.moveTo(-20, -10);
        cxt.lineTo(0, -10);
        cxt.lineTo(0, -20);
        cxt.lineTo(20, 0);
        cxt.lineTo(0, 20);
        cxt.lineTo(0, 10);
        cxt.lineTo(-20, 10);
        cxt.closePath();
        cxt.fill();
        cxt.restore();
   }
};
<!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/arrow.js"></script>
   <script type="text/javascript">
        function $$(id) {
             return document.getElementById(id);
        }
        window.onload = function () {
             var cnv = $$("canvas");
             var cxt = cnv.getContext("2d");

             //实例化一个箭头,中点坐标为画布中心坐标
             var arrow = new Arrow(cnv.width / 2, cnv.height / 2);
             //获取鼠标坐标
             var mouse = tools.getMouse(cnv);

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

                  var dx = mouse.x - cnv.width / 2;
                  var dy = mouse.y - cnv.height / 2;
                  //使用Math.atan2()方法计算出鼠标与箭头中心的夹角
                  arrow.angle = Math.atan2(dy, dx);

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

实时计算两点间的距离

dx = x2 - x1;
dy = y2 - y1;
distance = Math.sqrt(dx*dx + dy*dy);

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

             var x = cnv.width / 2;
             var y = cnv.height / 2;

             var mouse = tools.getMouse(cnv);
             (function frame() {
                  window.requestAnimationFrame(frame);
                  cxt.clearRect(0, 0, cnv.width, cnv.height);

                  cxt.save();
                  cxt.beginPath();
                  cxt.moveTo(x, y);
                  //mouse.x表示鼠标的x轴坐标,mouse.y表示鼠标的y轴坐标
                  cxt.lineTo(mouse.x, mouse.y);
                  cxt.closePath();
                  cxt.strokeStyle = "red";
                  cxt.stroke();
                  cxt.restore();

                  var dx = mouse.x - x;
                  var dy = mouse.y - y;
                  var distance = Math.sqrt(dx * dx + dy * dy);
                  text.innerText = "鼠标与中点距离为:" + distance;
             })();

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

圆周运动

正圆运动

通过圆心坐标和角度,可以计算出圆周上任意一点的坐标

封装【绘制球】的函数,写在新建 js/ball.js 中

function Ball(x,y,radius,color)
{
   //小球中心的x坐标,默认值为0
   this.x = x || 0;
   //小球中心的y坐标,默认值为0
   this.y = y || 0;
   //小球半径,默认值为12
   this.radius = radius || 12;
   //小球颜色,默认值为"#6699FF"
   this.color = color || "#6699FF";

   this.scaleX = 1;
   this.scaleY = 1;
}
Ball.prototype = {
   //绘制"描边"小球
   stroke: function (cxt) {
        cxt.save();
        cxt.scale(this.scaleX, this.scaleY);
        cxt.strokeStyle = this.color;
        cxt.beginPath();
        cxt.arc(this.x, this.y, this.radius, 0, 360 * Math.PI / 180, false);
        cxt.closePath();
        cxt.stroke();
        cxt.restore();
   },
   //绘制"填充"小球
   fill: function (cxt) {
        cxt.save();
        cxt.translate(this.x, this.y);
        cxt.rotate(this.rotation);
        cxt.scale(this.scaleX, this.scaleY);
        cxt.fillStyle = this.color;
        cxt.beginPath();
        cxt.arc(0, 0, this.radius, 0, 360 * Math.PI / 180, false);
        cxt.closePath();
        cxt.fill();
        cxt.restore();
   }
}

范例代码:

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

             //实例化一个小球,中心坐标为(100,25),半径、颜色都取默认值
             var ball = new Ball(100, 25);
             var centerX = cnv.width / 2;
             var centerY = cnv.height / 2;
             var radius = 50;
             var angle = 0;

             (function frame() {
                  window.requestAnimationFrame(frame);
                  cxt.clearRect(0, 0, cnv.width, cnv.height);
                  //绘制圆形
                  cxt.beginPath();
                  cxt.arc(centerX, centerY, 50, 0, 360 * Math.PI / 180, false);
                  cxt.closePath();
                  cxt.stroke();

                  //计算小球坐标
                  ball.x = centerX + Math.cos(angle) * radius;
                  ball.y = centerY + Math.sin(angle) * radius;
                  ball.fill(cxt);

                  //角度递增
                  angle += 0.05;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

椭圆运动

x = centerX + Math.cos(angle)*radiusX;
y = centerY + Math.sin(angle)*radiusY;

范例代码:

<!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(100, 25);
             var centerX = cnv.width / 2;
             var centerY = cnv.height / 2;
             var radiusX = 60;
             var radiusY = 40;
             var angle = 0;

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

                  //计算小球坐标
                  ball.x = centerX + Math.cos(angle) * radiusX;
                  ball.y = centerY + Math.sin(angle) * radiusY;
                  ball.fill(cxt);

                  //角度递增
                  angle += 0.05;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

左右摆动

x = centerX + Math.sin(angle) * range;
angle += speed;
  • (centerX,centerY)表示物体中心坐标
  • angle表示角度(弧度制)
  • range表示振幅
  • speed表示角度改变的大小。
<!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 angle = 0;
             var range = 80;

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

                  ball.x = cnv.width / 2 + Math.sin(angle) * range;
                  ball.fill(cxt);

                  //角度递增
                  angle += 0.05;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

上下波动前进

y = centerY + Math.sin(angle) * range;
angle += speed;
  • (centerX,centerY)表示物体中心坐标
  • angle表示角度(弧度制)
  • range表示振幅
  • speed表示角度改变的大小。
<!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 angle = 0;
             var range = 40;

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

                  ball.x += 1;
                  ball.y = cnv.height / 2 + Math.sin(angle) * range;
                  ball.fill(cxt);

                  //角度递增
                  angle += 0.05;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

放大缩小(脉冲)

scaleX = 1 + Math.sin(angle) * range;
scaleY = 1 + Math.sin(angle) * range;
angle += speed;
  • scaleX表示物体x轴方向缩放的倍数
  • scaleY表示物体y轴方向缩放的倍数
  • angle表示角度(弧度制)
  • range表示振幅
  • speed表示角度改变的大小
<!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, 25);
             var range = 0.5;
             var angle = 0;

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

                  ball.scaleX = 1 + Math.sin(angle) * range;
                  ball.scaleY = 1 + Math.sin(angle) * range;
                  ball.fill(cxt);

                  //角度递增
                  angle += 0.05;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

匀速直线运动

匀速直线运动——物体在一条直线上运动,并且物体在任何相等时间间隔内通过的位移是相等的。

匀速运动是一种加速度为0的运动。匀速运动只有一种,那就是:匀速直线运动。很多人以为“匀速圆周运动”也是匀速运动,其实这是错误的。事实上,匀速圆周运动准确来说应该是匀速率圆周运动或者匀角速度运动。它的加速度是不为0的,因此匀速圆周运动并不是匀速运动。


水平/垂直匀速直线运动

object.x + = vx;
objectobject.x表示物体x轴坐标
  • object.x表示物体x轴坐标
  • object.y表示物体y轴坐标
  • vx表示x轴方向的速度大小
  • vy表示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轴速度为2,也就是每帧向正方向移动2px
             var vx = 2;

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

                  ball.x += vx;

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

任意方向的匀速直线运动

使用速度分解实现

vx = speed * Math.cos(angle * Math.PI/180);
vy = speed * Math.sin(angle * Math.PI/180);
object.x += vx;
object.y += vy;
  • object.x表示物体x轴坐标
  • object.y表示物体y轴坐标
  • vx表示x轴方向的速度大小
  • vy表示y轴方向的速度大小
  • speed表示任意方向的速度大小
  • angle表示该速度的方向与x轴正方向的夹角
<!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();
             var speed = 2;
             //速度方向与x轴正方向角度为30°
             var vx = speed * Math.cos(30 * Math.PI / 180);
             var vy = speed * Math.sin(30 * Math.PI / 180);

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

                  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/arrow.js"></script>
   <script type="text/javascript">
        function $$(id) {
             return document.getElementById(id);
        }
        window.onload = function () {
             var cnv = $$("canvas");
             var cxt = cnv.getContext("2d");

             //实例化一个箭头,箭头中心坐标为画布中心坐标
             var arrow = new Arrow(cnv.width / 2, cnv.height / 2);
             var mouse = tools.getMouse(cnv);
             var speed = 1.5;
             var angle = 0;

             (function drawFrame() {
                  window.requestAnimationFrame(drawFrame, cnv);
                  cxt.clearRect(0, 0, cnv.width, cnv.height);
                  //计算出鼠标与箭头中心之间的夹角
                  var dx = mouse.x - cnv.width / 2;
                  var dy = mouse.y - cnv.height / 2;
                  angle = Math.atan2(dy, dx);

                  var vx = Math.cos(angle) * speed;
                  var vy = Math.sin(angle) * speed;
                  arrow.x += vx;
                  arrow.y += vy;

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

首先初始化速度(speed)和角度(angle),然后将鼠标当前坐标减去箭头的坐标,得到dx、dy,之后再使用Math.atan2()得到夹角度数,最后使用三角函数将速度分解为x和y两个方向的分速度。

加/减速直线运动

水平或垂直加/减速直线运动

加速运动,指的是方向相同、速度大小变化的运动。速度递增的是加速运动,速度递减的是减速运动。

加速度,指的是单位时间内速度改变的矢量。

vx += ax;
vy += ay;
object.x += vx;
object.y += vy;
  • (object.x,object.y) 为物体的坐标
  • vx表示x轴方向的速度大小
  • vy表示y轴方向的速度大小
  • ax表示x轴方向加速度,ay表示y轴方向加速度。当ax大于0时,物体向右做匀加速运动;当ax小于0时,物体向左做匀加速运动;当ax等于0时,物体按原来速度运动。ay跟ax是一样的。
<!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 vx = 0;
             var ax = 0.2;

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

                  ball.x += vx;
                  ball.fill(cxt);

                  vx += ax;
             })();
        }
   </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);
             //初始化x轴速度以及加速度
             var vx = 8;
             var ax = -0.2;

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

                  ball.x += vx;
                  ball.fill(cxt);

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

由于小球最开始的时候在x轴上有一个正方向的初始速度(varvx=8;),因此小球一开始会向右运动。但是由于加速度为负值,所以小球一开始向右做的是匀减速运动。当速度减到0的时候,此时加速度却不为0,接下来小球会继续向左做匀加速运动。

任意方向加/减速直线运动

分解加速度即可

ax = a * Math.cos(angle * Math.PI/180);
ay = a * Math.sin(angle * Math.PI/180);
vx += ax;
vy += ay;
object.x += vx;
object.y += vy;
  • (object.x,object.y) 为物体的坐标
  • vx表示x轴方向的速度大小
  • vy表示y轴方向的速度大小
  • ax表示x轴方向加速度
  • ay表示y轴方向加速度。
  • a表示任意方向的加速度大小
  • angle表示该加速度的方向与x轴正方向的夹角
<!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();
             var a = 0.2;
             //计算出x轴和y轴2个方向的加速度
             var ax = a * Math.cos(30 * Math.PI / 180);
             var ay = a * Math.sin(30 * Math.PI / 180);
             var vx = 0;
             var vy = 0;

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

                  ball.x += vx;
                  ball.y += vy;
                  ball.fill(cxt);

                  vx += ax;
                  vy += ay;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

重力

因地球引力引起的加速度,运动规律与加速运动相同。

vy += gravity;
object.y += vy;

抛物线运动

在x轴方向做的是匀速运动,在y轴方向做的是减加速运动(受到重力影响)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
   <title></title>
   <meta charset="utf-8" />
   <script src="js/utils.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);
             var vx = 4;
             var vy = -5;
             var gravity = 0.2;

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

                  ball.x += vx;
                  ball.y += vy;
                  ball.fill(cxt);

                  //变量递增
                  vy += gravity;
             })();
        }
   </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, 0);
             //y轴初始速度为0,重力加速度为0.2,反弹系数为-0.8
             var vy = 0;
             var gravity = 0.2;
             var bounce = -0.8;

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

                  ball.y += vy;
                  //边界检测
                  if (ball.y > cnv.height - ball.radius) {
                        ball.y = cnv.height - ball.radius;
                        //速度反向并且减小
                        vy = vy * bounce;
                  }
                  ball.fill(cxt);

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

if(ball.y>cnv.height-ball.radius){}是一个边界检测,表示当“小球y轴坐标”大于“画布高度减去小球半径”时执行的操作。

小球碰到地面都会反弹,由于反弹会有速度损耗,并且小球y轴速度方向会变为反方向,因此需要乘以一个反弹系数bounce。反弹系数取值一般为-1.0~0之间的任意数。

  • 反弹之后,速度方向变为相反方向了,所以反弹系数是负数呢
  • 反弹之后,速度只会变小,所以反弹系数取值为-1.0~0之间任意数,而不能是-2、-3

前抛落地反弹

在水平方向上加个速度即可

<!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);
             var vx = 3;
             var vy = -6;
             var gravity = 0.2;
             var bounce = -0.75;

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

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

                  //边界检测
                  if ((ball.y + ball.radius) > cnv.height) {
                         ball.y = cnv.height - ball.radius;
                         vy = vy * bounce;
                  }
                  ball.fill(cxt);

                  //变量递增
                  vy += gravity;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="300" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

摩擦力

摩擦力,指阻碍物体相对运动的力。其中摩擦力的方向与物体相对运动的方向相反。摩擦力只会改变速度的大小而不会改变它的方向。换句话说,摩擦力只能将物体的速度降至0,但它无法让物体掉头往相反的方向移动。

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);
             //初始化x轴方向速度为2,摩擦系数为0.95
             var vx = 8;
             var friction = 0.95;

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

                  ball.x += vx;
                  ball.fill(cxt);

                  //变量改变
                  vx *= friction;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>

  • 任意方向运动遇到摩擦力

当物体沿任意方向运动时,如果我们加入摩擦力因素,那么每次都是先把该方向的速度分解为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();
             var speed = 8;
             var vx = speed * Math.cos(30 * Math.PI / 180);
             var vy = speed * Math.sin(30 * Math.PI / 180);
             var friction = 0.95;

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

                  ball.x += vx;
                  ball.y += vy;
                  ball.fill(cxt);

                  //变量改变
                  vx *= friction;
                  vy *= friction;
             })();
        }
   </script>
</head>
<body>
   <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
</body>
</html>
目录
相关文章
|
2天前
|
前端开发 JavaScript
canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画
canvas系列教程07 ——捕获、拖拽、抛掷、缓动动画、弹性动画
7 1
|
8天前
|
资源调度 前端开发 JavaScript
web实现酷炫的canvas粒子动画背景
web实现酷炫的canvas粒子动画背景
7 0
|
2月前
|
移动开发 前端开发 JavaScript
canvas内容移动与交互
canvas内容移动与交互
|
2月前
|
前端开发 算法
canvas详解10-图形元素交互
canvas详解10-图形元素交互
41 1
|
2月前
|
前端开发 小程序
微信小程序canvas画布绘制;canvas画布图片保存
微信小程序canvas画布绘制;canvas画布图片保存
59 0
|
12月前
|
缓存 前端开发 小程序
微信小程序canvas画布渲染图片
微信小程序canvas画布渲染图片
186 0
|
移动开发 前端开发 JavaScript
【前端动画】实现动画的6种方式
【前端动画】实现动画的6种方式
1017 0
|
存储 前端开发 算法
canvas进阶——实现事件系统
前言 大家好! 我是热爱图形的fly, 之前在群里和粉丝讨论canvas 如何事件系统, 然后呢? 我自己其实也对这个比较感兴趣, 我看过很多canvas 实现的项目, 比如canvas 实现思维导图 「xmind」 , canvas 实现一个「绘图工具」。 然后呢无论是哪一个,其实背后都是在canavs 背后实现了一套事件系统,可惜这些源码都不开源。所以本着学习的激情, 我参考了一些文章实现一个简单事件系统。本篇文章你可以学到下面👇这些内容 我是怎么基于canvas去「构建基础框架」的 几何算法—— 「判断点是不是任意多边形内部」 如何进行「事件分发」和「阻止事件冒泡」 本篇文章我全是干
canvas进阶——实现事件系统
|
前端开发 JavaScript
将 WebGL 动画作为背景
大家好我是FLy哥,又到了周末了, 这次分享的文章不是很长,如果你正在或者将来会涉及到你的博客,就是如何在正常的网页中加入酷炫的3D效果: 图片 webgl 动画 上面的3D动画 其实就是整个网页的背景,然后你的html 页面 还是正常写的,互不干涉,其实还就是一个字, 让自己的页面看着更加花里胡哨的 图片 花里胡哨 我们看看如何解决吧??? 有 2 种方法: 方法1 将画布的 CSS 属性 position 设置成 fixed #canvas { position: fixed; left: 0; top: 0; z-index: -1; ...} 并将 z-index 设置成 -
将 WebGL 动画作为背景
|
存储 前端开发 JavaScript
教程—炫目的Off-Canvas滑动导航
Off-Canvas 滑动导航现在逐渐在移动页面变得越来越流行了,本文就将讨论如何通过 jQuery 增添、删除类来操作 CSS 过渡和动画完成这样的效果。
511 0
教程—炫目的Off-Canvas滑动导航

热门文章

最新文章

  • 1
    流量控制系统,用正则表达式提取汉字
    25
  • 2
    Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
    26
  • 3
    Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
    26
  • 4
    Redis07命令-String类型字符串,不管是哪种格式,底层都是字节数组形式存储的,最大空间不超过512m,SET添加,MSET批量添加,INCRBY age 2可以,MSET,INCRSETEX
    27
  • 5
    S外部函数可以访问函数内部的变量的闭包-闭包最简单的用不了,闭包是内层函数+外层函数的变量,简称为函数套函数,外部函数可以访问函数内部的变量,存在函数套函数
    24
  • 6
    Redis06-Redis常用的命令,模糊的搜索查询往往会对服务器产生很大的压力,MSET k1 v1 k2 v2 k3 v3 添加,DEL是删除的意思,EXISTS age 可以用来查询是否有存在1
    30
  • 7
    Redis05数据结构介绍,数据结构介绍,官方网站中看到
    22
  • 8
    JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
    20
  • 9
    JS数组操作---删除,arr.pop()方法从数组中删除最后一个元素,并返回该元素的值,arr.shift() 删除第一个值,arr.splice()方法,删除指定元素,arr.splice,从第一
    20
  • 10
    定义好变量,${age}模版字符串,对象可以放null,检验数据类型console.log(typeof str)
    19