Skip to content

canvas 动画 ​

2024-05-10 23:42:41

<script lang="ts" setup>
import { onMounted, ref } from 'vue'

const canvas = ref<HTMLCanvasElement>()

function getRandom(min: number, max: number) {
  return Math.floor(Math.random() * (max - min + 1) + min)
}
onMounted(() => {
  const ctx = canvas.value?.getContext('2d')

  class Point {
    constructor() {
      this.r = 4
      this.x = getRandom(0, canvas.value.width - this.r / 2)
      this.y = getRandom(0, canvas.value.height - this.r / 2)
      this.xSpeed = getRandom(-50, 50)
      this.ySpeed = getRandom(-50, 50)
      this.lastMoveTime = null
    }
    x: number
    y: number
    r: number
    xSpeed: number
    ySpeed: number
    lastMoveTime: number | null

    draw() {
      if (this.lastMoveTime) {
        const time = (Date.now() - this.lastMoveTime) / 1000
        this.x = this.x + this.xSpeed * time
        this.y = this.y + this.ySpeed * time
        // 边界检测
        if (this.x < 0 || this.x > canvas.value.width - this.r) {
          this.xSpeed = -this.xSpeed
        }
        if (this.y < 0 || this.y > canvas.value.height - this.r) {
          this.ySpeed = -this.ySpeed
        }
      }
      ctx?.beginPath()
      ctx?.arc(this.x, this.y, this.r, 0, Math.PI * 2)
      ctx!.fillStyle = 'rgb(200, 200, 200)'
      ctx?.fill()
      this.lastMoveTime = Date.now()
    }
  }

  class Graph {
    constructor(pointNumber = 15, maxDistance = 200) {
      this.points = new Array(pointNumber).fill(0).map(() => new Point())
      this.maxDistance = maxDistance
    }

    points: Point[]
    maxDistance: number

    draw() {
      if (!canvas) return
      requestAnimationFrame(() => {
        this.draw()
      })
      // 每次移动前清空画布
      ctx?.clearRect(0, 0, canvas.value.width, canvas.value.height)
      const length = this.points.length
      for (let i = 0; i < length; i++) {
        this.points[i].draw()
        for (let j = i + 1; j < length; j++) {
          const distance = Math.sqrt(
            Math.pow(this.points[i].x - this.points[j].x, 2) +
              Math.pow(this.points[i].y - this.points[j].y, 2),
          )
          if (distance < this.maxDistance) {
            ctx?.beginPath()
            ctx?.moveTo(this.points[i].x, this.points[i].y)
            ctx?.lineTo(this.points[j].x, this.points[j].y)
            ctx?.closePath()
            // 线的透明度随距离变化
            ctx!.strokeStyle = `rgba(200, 200, 200, ${
              1 - distance / this.maxDistance
            })`
            ctx?.stroke()
          }
        }
      }
    }
  }

  // 初始化
  function init() {
    if (!canvas) return
    canvas.value.width = 650
    canvas.value.height = 550
    canvas.value.style.border = '1px solid rgba(200, 200, 200)'
  }
  init()
  const graph = new Graph()
  graph.draw()
})
</script>

<template>
  <div>
    <canvas ref="canvas" />
  </div>
</template>

Released under the MIT License.