diff --git a/bun.lockb b/bun.lockb index f137ba7..b7bf71a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/index.html b/index.html index 44a9335..3504586 100644 --- a/index.html +++ b/index.html @@ -2,12 +2,11 @@ - - Vite + TS + ripples | compost.party -
+ diff --git a/package.json b/package.json index 335a73b..801587e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "devDependencies": { "typescript": "^5.2.2", + "typescript-language-server": "^4.3.1", "vite": "^5.0.8" } } diff --git a/src/counter.ts b/src/counter.ts deleted file mode 100644 index 09e5afd..0000000 --- a/src/counter.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function setupCounter(element: HTMLButtonElement) { - let counter = 0 - const setCounter = (count: number) => { - counter = count - element.innerHTML = `count is ${counter}` - } - element.addEventListener('click', () => setCounter(counter + 1)) - setCounter(0) -} diff --git a/src/main.ts b/src/main.ts index 791547b..ec0d437 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,24 +1,73 @@ import './style.css' -import typescriptLogo from './typescript.svg' -import viteLogo from '/vite.svg' -import { setupCounter } from './counter.ts' -document.querySelector('#app')!.innerHTML = ` -
- - - - - - -

Vite + TypeScript

-
- -
-

- Click on the Vite and TypeScript logos to learn more -

-
-` +const canvas = document.querySelector('#canvas')! -setupCounter(document.querySelector('#counter')!) +const ctx = canvas.getContext('2d')! + +const MIN_RADIUS = 20 +const MAX_RADIUS = 200 +const MAX_AGE = 5000 // in milliseconds + +type Particle = { + position: [number, number], + age: number +} + +const createParticle = (x: number, y: number): Particle => ({ + position: [x, y], + age: 0, +}) + +let state = { + particles: [] +} + +type State = typeof state + +canvas.addEventListener('click', (e) => { + // TODO Normalize x and y coords + const particle = createParticle(e.clientX, e.clientY) + state.particles.push(particle) +}) + +const update = (state: State, deltaTime: number): State => ({ + particles: state.particles + .map(p => ({ + ...p, + age: p.age + deltaTime + })) + .filter(p => p.age < MAX_AGE) +}) + +const render = (state: State, ctx: CanvasRenderingContext2D) => { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) + + for (const particle of state.particles) { + const progress = (particle.age / MAX_AGE) + const opacity = Math.min(1, (1-progress)*(1-progress)) + + ctx.beginPath() + ctx.strokeStyle = `rgba(40, 40, 40, ${opacity})` + const [x, y] = particle.position + const radius = (progress * (MAX_RADIUS - MIN_RADIUS)) + MIN_RADIUS + ctx.ellipse(x, y, radius, radius, 0, 0, Math.PI * 2) + ctx.stroke() + } +} + +let before = Date.now() +const loop = () => { + const now = Date.now() + const dt = now - before + state = update(state, dt) + render(state, ctx) + before = now + requestAnimationFrame(loop) +} + +document.addEventListener('DOMContentLoaded', () => { + canvas.width = canvas.parentElement!.clientWidth + canvas.height = canvas.parentElement!.clientHeight + ctx.translate(0.5, 0.5) + loop() +}) diff --git a/src/style.css b/src/style.css index f9c7350..92912df 100644 --- a/src/style.css +++ b/src/style.css @@ -1,96 +1,11 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - +html, body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; + min-width: 100wh; min-height: 100vh; + padding: 0; + margin: 0; } -h1 { - font-size: 3.2em; - line-height: 1.1; -} +canvas { -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.vanilla:hover { - filter: drop-shadow(0 0 2em #3178c6aa); -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } } diff --git a/src/typescript.svg b/src/typescript.svg deleted file mode 100644 index d91c910..0000000 --- a/src/typescript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file