| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- <template>
- <canvas ref="canvasRef" class="security-canvas"></canvas>
- </template>
- <script setup>
- import { ref, onMounted, onBeforeUnmount } from 'vue'
- const canvasRef = ref(null)
- let ctx = null
- let animationId = null
- let particles = []
- let width = 0
- let height = 0
- let dpr = 1
- function createParticles(count) {
- particles = []
- for (let i = 0; i < count; i++) {
- particles.push({
- x: Math.random() * width,
- y: Math.random() * height,
- vx: (Math.random() - 0.5) * 0.5,
- vy: -(0.2 + Math.random() * 1.2),
- size: 2,
- alpha: 0.4 + Math.random() * 0.6,
- sway: Math.random() * Math.PI * 2,
- swayAmp: 0.2 + Math.random() * 1.2
- })
- }
- }
- function resize() {
- const el = canvasRef.value
- if (!el) return
- const rect = el.getBoundingClientRect()
- width = Math.max(0, rect.width)
- height = Math.max(0, rect.height)
- dpr = window.devicePixelRatio || 1
- el.width = Math.max(1, Math.floor(width * dpr))
- el.height = Math.max(1, Math.floor(height * dpr))
- el.style.width = width + 'px'
- el.style.height = height + 'px'
- ctx = el.getContext('2d')
- // reset transform then apply devicePixelRatio scale so drawing coords stay in CSS px
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
- const area = width * height
- const count = Math.min(600, Math.max(60, Math.round(area / 8000)))
- createParticles(count)
- }
- function step() {
- if (!ctx) return
- ctx.clearRect(0, 0, width, height)
- for (let p of particles) {
- // horizontal sway + slight vx
- p.sway += 0.01 + Math.random() * 0.01
- p.x += p.vx + Math.sin(p.sway) * (p.swayAmp * 0.3)
- p.y += p.vy
- if (p.y < -10) {
- p.y = height + 10
- p.x = Math.random() * width
- }
- // draw 1px particle
- ctx.fillStyle = `rgba(255,255,255,${p.alpha})`
- // rounding to avoid subpixel blurry 1px
- ctx.fillRect(Math.round(p.x), Math.round(p.y), p.size, p.size)
- }
- animationId = requestAnimationFrame(step)
- }
- onMounted(() => {
- resize()
- animationId = requestAnimationFrame(step)
- window.addEventListener('resize', resize)
- })
- onBeforeUnmount(() => {
- window.removeEventListener('resize', resize)
- if (animationId) cancelAnimationFrame(animationId)
- })
- </script>
- <style scoped>
- .security-canvas {
- width: 100%;
- height: 100%;
- display: block;
- pointer-events: none;
- }
- </style>
|