You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.1 KiB
Vue

<script setup lang="ts">
import type { Fn } from '@vueuse/core'
const r180 = Math.PI
const r90 = Math.PI / 2
const r15 = Math.PI / 12
const color = '#88888825'
const el = $ref<HTMLCanvasElement>()
const { random } = Math
const size = reactive(useWindowSize())
let start = $ref<Fn>(() => { })
const init = $ref(4)
const len = $ref(6)
// let stopped = $ref(false)
function initCanvas(canvas: HTMLCanvasElement, width = 400, height = 400, _dpi?: number) {
const ctx = canvas.getContext('2d')
if (!ctx) {
return {}
}
const dpr = window.devicePixelRatio || 1
// @ts-expect-error vendor
const bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1
const dpi = _dpi || dpr / bsr
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
canvas.width = dpi * width
canvas.height = dpi * height
ctx.scale(dpi, dpi)
return { ctx, dpi }
}
function polar2cart(x = 0, y = 0, r = 0, theta = 0) {
const dx = r * Math.cos(theta)
const dy = r * Math.sin(theta)
return [x + dx, y + dy]
}
onMounted(async () => {
const canvas = el!
const { ctx } = initCanvas(canvas, size.width, size.height)
if (!ctx) {
return
}
const { width, height } = canvas
let steps: Fn[] = []
let prevSteps: Fn[] = []
let iterations = 0
const step = (x: number, y: number, rad: number) => {
const length = random() * len
const [nx, ny] = polar2cart(x, y, length, rad)
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(nx, ny)
ctx.stroke()
const rad1 = rad + random() * r15
const rad2 = rad - random() * r15
if (nx < -100 || nx > size.width + 100 || ny < -100 || ny > size.height + 100)
return
if (iterations <= init || random() > 0.5) {
steps.push(() => step(nx, ny, rad1))
}
if (iterations <= init || random() > 0.5) {
steps.push(() => step(nx, ny, rad2))
}
}
let lastTime = performance.now()
const interval = 1000 / 40
let controls: ReturnType<typeof useRafFn>
const frame = () => {
if (performance.now() - lastTime < interval) {
return
}
iterations += 1
prevSteps = steps
steps = []
lastTime = performance.now()
if (!prevSteps.length) {
controls.pause()
// stopped = true
}
prevSteps.forEach(i => i())
}
controls = useRafFn(frame, { immediate: false })
start = () => {
controls.pause()
iterations = 0
ctx.clearRect(0, 0, width, height)
ctx.lineWidth = 1
ctx.strokeStyle = color
prevSteps = []
steps = [
() => step(random() * size.width, 0, r90),
() => step(random() * size.width, size.height, -r90),
() => step(0, random() * size.height, 0),
() => step(size.width, random() * size.height, r180),
]
if (size.width < 500) {
steps = steps.slice(0, 2)
}
controls.resume()
// stopped = false
}
start()
})
</script>
<template>
<div class="pointer-events-none fixed bottom-0 left-0 right-0 top-0">
<canvas ref="el" width="400" height="400" />
</div>
</template>