粒子时钟

39 阅读1分钟
<template>
  <div class="canvas-clock">
    <canvas id="canvas"></canvas>
  </div>
</template>

<script>
  export default {
    components: {},

    data() {
      return {
        animationMoveId: null,
        animationDrawId: null,
      }
    },

    mounted() {
      let canvas = null
      let ctx = null
      let particles = []
      let text = ''
      let _this = this
      canvas = document.getElementById('canvas')
      ctx = canvas.getContext('2d', { willReadFrequently: true })
      function initCanvas() {
        canvas.width = canvas.clientWidth
        canvas.height = canvas.clientHeight
      }
      initCanvas()
      class Particle {
        constructor() {
          this.size = getRandom(2*devicePixelRatio, 4*devicePixelRatio)
          const r = Math.min(canvas.width, canvas.height) / 2
          const rad = (getRandom(0, 360) * Math.PI) / 180
          const cx = canvas.width / 2
          const cy = canvas.height / 2
          this.x = cx + r * Math.cos(rad)
          this.y = cy + r * Math.sin(rad)
        }

        draw() {
          ctx.beginPath()
          ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)
          ctx.fillStyle = '#5445544d'
          ctx.fill()
        }

        moveTo(tx, ty) {
          const duration = 500
          const sx = this.x
          const sy = this.y
          const speedX = (tx - sx) / duration
          const speedY = (ty - sy) / duration
          const startTime = Date.now()
          // console.log(tx, ty, 'ty')
          const _move = () => {
            const t = Date.now() - startTime
            // console.log(t, 't')
            const x = sx + speedX * t
            const y = sy + speedY * t
            this.x = x
            this.y = y
            if (t >= duration) {
              this.x = tx
              this.y = ty
              return
            }
           _this.animationMoveId =  requestAnimationFrame(_move)

          }
          _move()
        }
      }
      // 获取[min,max]范围内的随机数
      function getRandom(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min
      }
      function getText() {
        return new Date().toTimeString().slice(0, 8)
      }
      function getPoint() {
        const points = []
        const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data
        // console.log(data,'data')
        const gap = 2
        for (let i = 0; i < canvas.width; i += gap) {
          for (let j = 0; j < canvas.height; j += gap) {
            const index = (i + j * canvas.width) * 4
            const r = data[index]
            const g = data[index + 1]
            const b = data[index + 2]
            const a = data[index + 3]
            if (r === 0 && g === 0 && b === 0 && a === 255) {
              points.push([i, j])
            }
          }
        }
        return points
      }
      function clear() {
        // console.log('clear',canvas.width, canvas.height)
        ctx.clearRect(0, 0, canvas.width, canvas.height)
      }
      function update() {
        const curText = getText()
        if (text === curText) return
        clear()
        text = curText
        // console.log(text,'text')
        const { width, height } = canvas
        ctx.font = `${140 * devicePixelRatio}px 'Roboto Mono', sans-serif`
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.fillStyle = '#000'
        ctx.fillText(text, width / 2, height / 2)
        const points = getPoint()
        clear()
        for (let i = 0; i < points.length; i++) {
          const [x, y] = points[i]
          let p = particles[i]
          if (!p) {
            p = new Particle()
            particles.push(p)
          }
          p.moveTo(x, y)
        }
        if ( points.length < particles.length) {
          particles.splice(points.length)
        }
      }
      function draw() {
        clear()
        update()
        for (const p of particles) {
          p.draw()
        }
       _this.animationDrawId =  requestAnimationFrame(draw)
      }
      draw()
    },

    methods: {},
    destroyed() {
      console.log('destroyed')
      cancelAnimationFrame(this.animationDrawId)
      cancelAnimationFrame(this.animationMoveId)
      this.animationDrawId = null
      this.animationMoveId = null
    },
    deactivated() {
      console.log('deactivated')
      cancelAnimationFrame(this.animationDrawId)
      cancelAnimationFrame(this.animationMoveId)
      this.animationDrawId = null
      this.animationMoveId = null
    },
  }
</script>

<style lang="less" scoped>
  .canvas-clock {
    width: 100%;
    height: 100%;
    padding: 20px;
  }
  #canvas {
    width: 100%;
    height: 100%;
    display: block;
    // background: radial-gradient(#fff, #8c738c);
  }
</style>