添加物理元素

TIP

可以根据一些物理现象进行交互演示,比如小球的弹跳,下落过程中受到重力的影响速度增加,碰撞到边缘区域速度反向,用鼠标点击小球获取一定速度,拖动鼠标抛出小球等。

需求:

  1. 点击“添加小球”按钮,会在画布上添加一个小球。
  2. 添加的小球会向右下做自由落体运动,碰到地板、墙壁后会反弹。同时每次反弹会受到阻力的影响,稍稍减少速度。
  3. 鼠标点击任意小球,会让该小球突然加速,让它向某个方向弹开。
  4. 在画布空白区域拖动鼠标可以添加一定速度的小球
  5. 点击“清空画布”按钮,会清除页面上的所有小球。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>test</title>
  <style>
    canvas {
      cursor: pointer;
      border: 1px solid black;
    }
  </style>
  <script>
    class Ball {
      constructor (x, y, dx, dy, radius) {
        // 圆心与半径
        this.x = x;
        this.y = y;
        this.radius = radius;
        // 速度
        this.dx = dx;
        this.dy = dy;
        // 颜色
        this.strokeColor = "black";
        this.fillColor = "red";
      }
    }
    class Point {
      constructor (x, y) {
        this.x = x;
        this.y = y;
      }
    }
    // 这个数组用于保存画布上出现的所有球
    var balls = [];
    var canvas;
    var context;
    window.onload = function () {
      canvas = document.getElementById("canvas");
      context = canvas.getContext("2d");
      canvas.onmousedown = canvasClick;
      canvas.onmouseup = mouseUp;
      // 每0.02秒绘制一次画布
      setTimeout(drawFrame, 20);
    };
    // 添加小球
    function addBall(x, y, dx, dy) {
      // 小球半径
      var radius = 20;
      // 创建新的ball对象
      var ball = new Ball(x || 50, y || 50, dx || Math.random() * 10, dy ||Math.random() * 10, radius);
      // 将其保存在balls数组中
      balls.push(ball);
    }
    // 清除画布
    function clearBalls() {
      // 删除所有球对象
      balls = [];
    }
    // 重绘画布帧
    function drawFrame() {
      // 清除画布
      context.clearRect(0, 0, canvas.width, canvas.height);
      // 循环所有的球
      for (var i = 0; i < balls.length; i++) {
        // 把每个球移动到新的位置
        var ball = balls[i];
        ball.x += ball.dx;
        ball.y += ball.dy;
        // 添加摩擦力作用的效果,减慢左右移动速度
        ball.dx = ball.dx * 0.998;
        // 边界检测,x轴反弹
        if ((ball.x + ball.radius > canvas.width) || (ball.x - ball.radius < 0)) {
          ball.dx = -ball.dx;
        }
        // 边界检测,y轴反弹
        if ((ball.y + ball.radius > canvas.height) || (ball.y - ball.radius < 0)) {
          ball.dy = -ball.dy * 0.9;
        } else if ((ball.y) < canvas.height) {
          // 添加重力作用的效果,让球加速下落
          ball.dy += 0.22;
        }
        // 绘制球
        context.beginPath();
        context.fillStyle = ball.fillColor;
        context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
        context.lineWidth = 1;
        context.fill();
        context.stroke();
      }
      // 20毫秒后绘制下一帧
      setTimeout(drawFrame, 1);
    }
    // 记录上一个点
    var prePoint;
    // 点击小球获取速度或者添加小球
    function canvasClick(e) {
      // 获取点击的坐标
      var clickX = e.pageX - canvas.offsetLeft;
      var clickY = e.pageY - canvas.offsetTop;
      prePoint = new Point(clickX, clickY)
      // 找到点中的小球
      for (var i in balls) {
        var ball = balls[i];
        if ((clickX > (ball.x - ball.radius)) && (clickX < (ball.x + ball.radius))) {
          if ((clickY > (ball.y - ball.radius)) && (clickY < (ball.y + ball.radius))) {
            // 改变点击的小球速度
            ball.dx -= 2;
            ball.dy -= 3;
            return;
          }
        }
      }
    }
    // 移动鼠标添加小球
    function mouseUp(e) {
      // 获取点击的坐标
      var clickX = e.pageX - canvas.offsetLeft;
      var clickY = e.pageY - canvas.offsetTop;
      var curPoint = new Point(clickX, clickY)
      // 获取速度
      var deltaX = curPoint.x - prePoint.x
      var deltaY = curPoint.y - prePoint.y
      // 添加小球
      if (deltaX !== 0 && deltaY !== 0) {
        addBall(curPoint.x, curPoint.y, deltaX / 10, deltaY / 10)
      }
    }
  </script>
</head>
<body>
  <canvas id="canvas" width="400" height="300">
  </canvas>
  <div>
    <button onclick="addBall()">添加小球</button>
    <button onclick="clearBalls()">清空画布</button>
  </div>
</body>
</html>

代码说明:

  1. 首先设置一个定时器,反复调用绘图函数(一般每秒30~40次)。每次调用,都会重绘整个画布。完成后的效果就像动画一样,每一帧间过渡会平滑而流畅。
  2. 有两种方法都可以实现重复绘制:setTimeout()和setInterval()。用哪个可以自行决定。
  3. setInterval() 能保证精确地按时重绘,当又可能因此牺牲性能(如果绘图代码执行时间比设定时间还要长,浏览器将很难跟上,随着绘图代码的连续执行,页面会出现短暂地停顿)

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress