初始提交:OCI 面板前端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { onMounted, onUnmounted, type Ref } from 'vue'
|
||||
|
||||
interface ParticleOptions {
|
||||
/** 粒子数;缺省按画布面积自适应(约每 8000px² 一粒) */
|
||||
count?: number
|
||||
}
|
||||
|
||||
const PALETTE = ['rgba(201,100,66,', 'rgba(135,134,127,', 'rgba(106,155,204,']
|
||||
|
||||
/** 背景飘浮粒子:缓慢上浮 + 透明度呼吸;遵循 prefers-reduced-motion,卸载即停 */
|
||||
export function useParticles(canvasRef: Ref<HTMLCanvasElement | null>, opts: ParticleOptions = {}) {
|
||||
let raf = 0
|
||||
onMounted(() => {
|
||||
const cv = canvasRef.value
|
||||
if (!cv || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
|
||||
const ctx = cv.getContext('2d')
|
||||
if (!ctx) return
|
||||
cv.width = cv.offsetWidth
|
||||
cv.height = cv.offsetHeight
|
||||
const W = cv.width
|
||||
const H = cv.height
|
||||
const count = opts.count ?? Math.round((W * H) / 8000)
|
||||
const ps = Array.from({ length: count }, (_, i) => ({
|
||||
x: Math.random() * W,
|
||||
y: Math.random() * H,
|
||||
r: 0.7 + Math.random() * 1.9,
|
||||
v: 0.08 + Math.random() * 0.34,
|
||||
a: 0.12 + Math.random() * 0.38,
|
||||
c: PALETTE[i % PALETTE.length],
|
||||
p: Math.random() * 6.28,
|
||||
}))
|
||||
const tick = (t: number) => {
|
||||
ctx.clearRect(0, 0, W, H)
|
||||
for (const p of ps) {
|
||||
p.y -= p.v
|
||||
if (p.y < -4) {
|
||||
p.y = H + 4
|
||||
p.x = Math.random() * W
|
||||
}
|
||||
const alpha = p.a * (0.6 + 0.4 * Math.sin(t / 1300 + p.p))
|
||||
ctx.beginPath()
|
||||
ctx.arc(p.x, p.y, p.r, 0, 6.28318)
|
||||
ctx.fillStyle = `${p.c}${alpha})`
|
||||
ctx.fill()
|
||||
}
|
||||
raf = requestAnimationFrame(tick)
|
||||
}
|
||||
raf = requestAnimationFrame(tick)
|
||||
})
|
||||
onUnmounted(() => cancelAnimationFrame(raf))
|
||||
}
|
||||
Reference in New Issue
Block a user