Discogenerator
Nu blir det fredagsdisco!
TL;DR
- Varje ruta i en grid får en slumpmässig
rgb(...)-färg, och custom-hookenuseIntervalsätter om alla färger vid varje takt. - Frekvensen i hertz översätts till intervallets delay som
1000 / frequency; under 1 Hz skickasnullin och discot pausas. - CSS grid-layouten och varje rutas färg sätts med inline-stilar, som passar värden som ändras vid varje rendering bättre än CSS-klasser.
Testa att ändra några av värdena nedan och se vad som händer med discolamporna:
Bygga en discogenerator
Den här discogeneratorn är byggd med React och TypeScript. Discolamporna drivs av custom-hooken useInterval, som har en egen genomgång.
Slumpmässiga färger
Varje ruta behöver sin egen färg:
function randomColor() {
const red = Math.floor(Math.random() * 256)
const green = Math.floor(Math.random() * 256)
const blue = Math.floor(Math.random() * 256)
return `rgb(${red}, ${green}, ${blue})`
}
function buildInitialColors(count: number) {
return Array.from({ length: count }, randomColor)
}
randomColor slumpar tre kanalvärden mellan 0 och 255 och sätter ihop en rgb(...)-sträng. buildInitialColors anropar den en gång per ruta, så en grid med rows * columns rutor får lika många slumpfärger.
Blinka vid varje takt
Lamporna byts på en timer som drivs av useInterval-hooken:
const isRunning = frequency >= 1
const transitionDuration = 1 / frequency / 2
useInterval(
() => setColors(buildInitialColors(rows * columns)),
isRunning ? 1000 / frequency : null,
)
Frekvensen anges i takter per sekund (Hz), så intervallets delay blir 1000 / frequency millisekunder. Varje takt bygger om alla färger på en gång. Att skicka in null när frekvensen sjunker under 1 pausar discot – useInterval river ner timern på egen hand. transitionDuration är en halv takt, så varje färg hinner tona in helt innan nästa ersätter den.
Bygga upp griden
Rutorna ligger i en CSS-grid, där både layouten och färgerna sätts inline:
<section
style={{
display: "grid",
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gridTemplateRows: `repeat(${rows}, ${height}px)`,
}}
>
{Array.from({ length: rows * columns }).map((_, index) => (
<div
key={index}
style={{
backgroundColor: colors[index],
transition: `background-color ${transitionDuration}s linear`,
}}
/>
))}
</section>
Grid:ens kolumner, rader och radhöjd beräknas från state, och varje cells bakgrundsfärg och transition uppdateras vid varje tick. Inline-stilar passar bra för värden som ändras ofta (som färg) eftersom de inte kräver att CSS-klasser räknas om vid varje ändring.
Allt tillsammans
Här är koden i sin helhet:
import { useInterval } from "hooks/useInterval"
import { useState } from "react"
function randomColor() {
const red = Math.floor(Math.random() * 256)
const green = Math.floor(Math.random() * 256)
const blue = Math.floor(Math.random() * 256)
return `rgb(${red}, ${green}, ${blue})`
}
function buildInitialColors(count: number) {
return Array.from({ length: count }, randomColor)
}
export const DiscoGenerator = () => {
const [columns, setColumns] = useState(3)
const [rows, setRows] = useState(3)
const [height, setHeight] = useState(100)
const [frequency, setFrequency] = useState(1)
const [colors, setColors] = useState(buildInitialColors(rows * columns))
const isRunning = frequency >= 1
const transitionDuration = isRunning ? 1 / frequency / 2 : 0
useInterval(
() => setColors(buildInitialColors(rows * columns)),
isRunning ? 1000 / frequency : null,
)
return (
<>
{/* inputs for rows, columns, frequency and height */}
<section
style={{
display: "grid",
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gridTemplateRows: `repeat(${rows}, ${height}px)`,
}}
>
{Array.from({ length: rows * columns }).map((_, index) => (
<div
key={index}
style={{
backgroundColor: colors[index],
transition: `background-color ${transitionDuration}s linear`,
}}
/>
))}
</section>
</>
)
}
Det finns en massa möjligheter här. Jag kommer utforska mer framöver!
