<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()
const _move = () => {
const t = Date.now() - startTime
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()
}
}
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
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() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
function update() {
const curText = getText()
if (text === curText) return
clear()
text = curText
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>