mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Chore: Port over Settings (Barely Works)
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
const CARD_COUNT = 18
|
||||
const CARD_W = 52
|
||||
const CARD_H = 72
|
||||
const CARD_RADIUS = 6
|
||||
const DRIFT_SPEED = 0.018
|
||||
|
||||
interface Card {
|
||||
x: number
|
||||
y: number
|
||||
vx: number
|
||||
vy: number
|
||||
rot: number
|
||||
vrot: number
|
||||
opacity: number
|
||||
scale: number
|
||||
hue: number
|
||||
}
|
||||
|
||||
function makeCard(w: number, h: number): Card {
|
||||
const side = Math.floor(Math.random() * 4)
|
||||
const margin = 80
|
||||
let x = 0, y = 0
|
||||
if (side === 0) { x = Math.random() * w; y = -margin }
|
||||
if (side === 1) { x = w + margin; y = Math.random() * h }
|
||||
if (side === 2) { x = Math.random() * w; y = h + margin }
|
||||
if (side === 3) { x = -margin; y = Math.random() * h }
|
||||
const cx = w / 2, cy = h / 2
|
||||
const dx = cx - x, dy = cy - y
|
||||
const len = Math.sqrt(dx * dx + dy * dy) || 1
|
||||
const spd = 0.12 + Math.random() * 0.1
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
vx: (dx / len) * spd * (0.3 + Math.random() * 0.4),
|
||||
vy: (dy / len) * spd * (0.3 + Math.random() * 0.4),
|
||||
rot: Math.random() * Math.PI * 2,
|
||||
vrot: (Math.random() - 0.5) * 0.006,
|
||||
opacity: 0.025 + Math.random() * 0.055,
|
||||
scale: 0.7 + Math.random() * 0.7,
|
||||
hue: 120 + Math.random() * 40,
|
||||
}
|
||||
}
|
||||
|
||||
function drawCard(ctx: CanvasRenderingContext2D, c: Card) {
|
||||
ctx.save()
|
||||
ctx.globalAlpha = c.opacity
|
||||
ctx.translate(c.x, c.y)
|
||||
ctx.rotate(c.rot)
|
||||
ctx.scale(c.scale, c.scale)
|
||||
|
||||
const w = CARD_W, h = CARD_H, r = CARD_RADIUS
|
||||
const x = -w / 2, y = -h / 2
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x + r, y)
|
||||
ctx.lineTo(x + w - r, y)
|
||||
ctx.quadraticCurveTo(x + w, y, x + w, y + r)
|
||||
ctx.lineTo(x + w, y + h - r)
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
|
||||
ctx.lineTo(x + r, y + h)
|
||||
ctx.quadraticCurveTo(x, y + h, x, y + h - r)
|
||||
ctx.lineTo(x, y + r)
|
||||
ctx.quadraticCurveTo(x, y, x + r, y)
|
||||
ctx.closePath()
|
||||
|
||||
ctx.strokeStyle = `hsla(${c.hue}, 28%, 62%, 0.9)`
|
||||
ctx.lineWidth = 1 / c.scale
|
||||
ctx.stroke()
|
||||
|
||||
const grad = ctx.createLinearGradient(x, y, x, y + h)
|
||||
grad.addColorStop(0, `hsla(${c.hue}, 20%, 40%, 0.18)`)
|
||||
grad.addColorStop(1, `hsla(${c.hue}, 20%, 20%, 0.06)`)
|
||||
ctx.fillStyle = grad
|
||||
ctx.fill()
|
||||
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
export function mountCardCanvas(canvas: HTMLCanvasElement) {
|
||||
const ctx = canvas.getContext('2d')!
|
||||
let cards = Array.from({ length: CARD_COUNT }, () => makeCard(canvas.offsetWidth || 800, canvas.offsetHeight || 600))
|
||||
let raf = 0
|
||||
let running = true
|
||||
|
||||
function resize() {
|
||||
const dpr = window.devicePixelRatio || 1
|
||||
canvas.width = canvas.offsetWidth * dpr
|
||||
canvas.height = canvas.offsetHeight * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
cards = Array.from({ length: CARD_COUNT }, () => makeCard(canvas.offsetWidth, canvas.offsetHeight))
|
||||
}
|
||||
|
||||
function tick() {
|
||||
if (!running) return
|
||||
const w = canvas.offsetWidth, h = canvas.offsetHeight
|
||||
ctx.clearRect(0, 0, w, h)
|
||||
for (const c of cards) {
|
||||
c.x += c.vx
|
||||
c.y += c.vy
|
||||
c.rot += c.vrot
|
||||
const pad = 120
|
||||
if (c.x < -pad || c.x > w + pad || c.y < -pad || c.y > h + pad) {
|
||||
Object.assign(c, makeCard(w, h))
|
||||
}
|
||||
drawCard(ctx, c)
|
||||
}
|
||||
raf = requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
const ro = new ResizeObserver(resize)
|
||||
ro.observe(canvas)
|
||||
resize()
|
||||
tick()
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
running = false
|
||||
cancelAnimationFrame(raf)
|
||||
ro.disconnect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function ringGeometry(r: number, pad: number) {
|
||||
const size = (r + pad) * 2
|
||||
const c = size / 2
|
||||
const circ = 2 * Math.PI * r
|
||||
return { size, c, circ }
|
||||
}
|
||||
|
||||
const RING_STEPS = [
|
||||
{ target: 0.15, duration: 400 },
|
||||
{ target: 0.45, duration: 800 },
|
||||
{ target: 0.72, duration: 600 },
|
||||
{ target: 0.88, duration: 1000 },
|
||||
{ target: 0.96, duration: 700 },
|
||||
]
|
||||
|
||||
export function animateRingProgress(onProgress: (p: number) => void): () => void {
|
||||
let current = 0.025
|
||||
let stepIdx = 0
|
||||
let start = performance.now()
|
||||
let raf = 0
|
||||
let stopped = false
|
||||
|
||||
function ease(t: number) {
|
||||
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
|
||||
}
|
||||
|
||||
function tick(now: number) {
|
||||
if (stopped) return
|
||||
if (stepIdx >= RING_STEPS.length) return
|
||||
|
||||
const step = RING_STEPS[stepIdx]
|
||||
const elapsed = now - start
|
||||
const t = Math.min(elapsed / step.duration, 1)
|
||||
const from = stepIdx === 0 ? 0.025 : RING_STEPS[stepIdx - 1].target
|
||||
current = from + (step.target - from) * ease(t)
|
||||
onProgress(current)
|
||||
|
||||
if (t >= 1) {
|
||||
stepIdx++
|
||||
start = now
|
||||
}
|
||||
|
||||
raf = requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
raf = requestAnimationFrame(tick)
|
||||
return () => { stopped = true; cancelAnimationFrame(raf) }
|
||||
}
|
||||
Reference in New Issue
Block a user