Hoppa till huvudinnehåll

Discogenerator

· 3 min att läsa
Filip Tammergård
Programmerare på Frilans Finans

Nu blir det fredagsdisco!

TL;DR

  • Varje ruta i en grid får en slumpmässig rgb(...)-färg, och custom-hooken useInterval sätter om alla färger vid varje takt.
  • Frekvensen i hertz översätts till intervallets delay som 1000 / frequency; under 1 Hz skickas null in 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!

Referenser