SOURCE

console 命令行工具 X clear

                    
>
console
 // 获取canvas画布和context
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    // 封装一个小球类
    class Ball {
      constructor(x, y, radius) {
        this.x = x
        this.y = y
        this.radius = radius
        this.angle = Math.random() * 180
        this.speed = 5
        this.flag = false
      }

      draw(ctx) {
        ctx.beginPath()
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
        ctx.closePath()
        ctx.fillStyle = 'blue'
        ctx.fill()
      }
    }

    // 随机生成若干个小球
    const balls = []
    while (balls.length < 10) {
      const radius = Math.random() * 20 + 10 // 10 ~ 30
      const x = Math.random() * (canvas.width - radius - radius) + radius
      const y = Math.random() * (canvas.height - radius - radius) + radius

      let flag = true
      for (let i = 0; i < balls.length; i++) {
        const dx = x - balls[i].x
        const dy = y - balls[i].y
        const dl = Math.sqrt(dx * dx + dy * dy)
        if (dl <= radius + balls[i].radius) {
          flag = false
        }
      }
      if (flag) {
        balls.push(new Ball(x, y, radius))
      }
    }

    function drawFrame () {
      window.requestAnimationFrame(drawFrame, canvas)
      canvas.height = canvas.height // 清空画布

      // 绘制墙壁
      ctx.strokeRect(0, 0, canvas.width, canvas.height)

      for (let i = 0; i < balls.length; i++) {
        const ball = balls[i]

        // 判断小球间的碰撞
        for (let j = i + 1; j < balls.length; j++) {
          const dx = ball.x - balls[j].x
          const dy = ball.y - balls[j].y
          const dl = Math.sqrt(dx * dx + dy * dy)
          if (dl <= ball.radius + balls[j].radius) {
            ball.flag === false ? ball.angle = ball.angle - 180 : ''
            balls[j].flag === false ? balls[j].angle = balls[j].angle - 180 : ''
            ball.flag = balls[j].flag = true
          }
        }

        // 判断与墙壁的碰撞反弹
        if (ball.flag === false) {
          if (ball.x + ball.radius > canvas.width) {
            ball.angle = 180 - ball.angle
          }
          if (ball.x - ball.radius < 0) {
            ball.angle = -(180 + ball.angle)
          }
          if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) {
            ball.angle = -ball.angle
          }
        }

        // 计算小球下一帧的坐标
        ball.x += ball.speed * Math.cos(ball.angle * Math.PI / 180)
        ball.y -= ball.speed * Math.sin(ball.angle * Math.PI / 180)

        // 绘制
        ball.draw(ctx)
        ball.flag = false
      }
    }

    drawFrame()
	<canvas id="canvas" width="300" height="300"></canvas>