馃寛 Animowana plazma w JavaScript

Efekt plazmy to klasyczny efekt graficzny, kt贸ry polega na tworzeniu p艂ynnych, kolorowych animacji przypominaj膮cych wiruj膮ce chmury lub p艂omienie. W tym tutorialu nauczysz si臋, jak stworzy膰 taki efekt od podstaw u偶ywaj膮c czystego JavaScript i elementu <canvas>.

Krok 1: Struktura HTML

Zacznijmy od podstawowej struktury HTML z elementem canvas:

<canvas id="plasmaCanvas" width="400" height="400"></canvas>

Krok 2: Inicjalizacja Canvas

Pierwszym krokiem jest uzyskanie kontekstu 2D canvas oraz przygotowanie bufora pikseli:

const canvas = document.getElementById('plasmaCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;

// Tworzymy obiekt ImageData do manipulacji pikselami
const imageData = ctx.createImageData(width, height);
const data = imageData.data; // Tablica RGBA pikseli
馃挕 Wskaz贸wka: Tablica data zawiera warto艣ci RGBA dla ka偶dego piksela. Ka偶dy piksel ma 4 elementy: czerwony, zielony, niebieski i Alpha.

Krok 3: Matematyka za Plazm膮

Efekt plazmy opiera si臋 na nak艂adaniu na siebie funkcji sinusoidalnych. Dla ka偶dego piksela (x, y) obliczamy warto艣膰 u偶ywaj膮c kombinacji funkcji sin() i cos():

function plasma(x, y, time) {
    // R贸偶ne fale sinusoidalne z r贸偶nymi cz臋stotliwo艣ciami
    const value = 
        Math.sin(x / 16.0 + time) +
        Math.sin(y / 8.0 + time) +
        Math.sin((x + y) / 16.0 + time) +
        Math.sin(Math.sqrt(x * x + y * y) / 8.0 + time);
    
    // Normalizujemy warto艣膰 do zakresu 0-1
    return (value + 4) / 8;
}

Ka偶da z tych funkcji sinusoidalnych tworzy inny wz贸r:

Krok 4: Mapowanie Kolor贸w

Konwertujemy warto艣膰 plazmy (0-1) na kolory RGB u偶ywaj膮c funkcji sinusoidalnych z przesuni臋ciem fazowym:

function getColor(value) {
    // U偶ywamy sin() z r贸偶nymi przesuni臋ciami dla RGB
    const r = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2));
    const g = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 2));
    const b = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 4));
    
    return [r, g, b];
}

Krok 5: Renderowanie

Teraz 艂膮czymy wszystko w funkcj臋 renderuj膮c膮:

function render(time) {
    // Konwertujemy czas na sekundy i spowalniamy
    time = time / 1000;
    
    // Dla ka偶dego piksela
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            // Obliczamy warto艣膰 plazmy
            const value = plasma(x, y, time);
            
            // Konwertujemy na kolor
            const [r, g, b] = getColor(value);
            
            // Ustawiamy piksel w buforze
            const index = (y * width + x) * 4;
            data[index] = r;       // Red
            data[index + 1] = g;   // Green
            data[index + 2] = b;   // Blue
            data[index + 3] = 255; // Alpha (pe艂na nieprzezroczysto艣膰)
        }
    }
    
    // Rysujemy bufor na canvas
    ctx.putImageData(imageData, 0, 0);
}

Krok 6: P臋tla Animacji

Ostatni krok to stworzenie p臋tli animacji u偶ywaj膮c requestAnimationFrame:

function animate(time) {
    render(time);
    requestAnimationFrame(animate);
}

// Startujemy animacj臋
requestAnimationFrame(animate);

Demo: Zobacz Efekt w Akcji

Pe艂ny Kod

Oto kompletny kod 藕r贸d艂owy efektu plazmy:

const canvas = document.getElementById('plasmaCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const imageData = ctx.createImageData(width, height);
const data = imageData.data;

function plasma(x, y, time) {
    const value = 
        Math.sin(x / 16.0 + time) +
        Math.sin(y / 8.0 + time) +
        Math.sin((x + y) / 16.0 + time) +
        Math.sin(Math.sqrt(x * x + y * y) / 8.0 + time);
    return (value + 4) / 8;
}

function getColor(value) {
    const r = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2));
    const g = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 2));
    const b = Math.floor(128 + 128 * Math.sin(value * Math.PI * 2 + 4));
    return [r, g, b];
}

function render(time) {
    time = time / 1000;
    
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const value = plasma(x, y, time);
            const [r, g, b] = getColor(value);
            const index = (y * width + x) * 4;
            data[index] = r;
            data[index + 1] = g;
            data[index + 2] = b;
            data[index + 3] = 255;
        }
    }
    
    ctx.putImageData(imageData, 0, 0);
}

function animate(time) {
    render(time);
    requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

Eksperymenty i Modyfikacje

Teraz, gdy masz dzia艂aj膮c膮 plazm臋, mo偶esz eksperymentowa膰 z r贸偶nymi parametrami:

1. Zmie艅 cz臋stotliwo艣膰 fal

Modyfikuj warto艣ci dzielnik贸w (16.0, 8.0) w funkcji plasma(), aby uzyska膰 wi臋ksze lub mniejsze wzory.

2. Dodaj wi臋cej fal

Dodaj kolejne funkcje sinusoidalne do sumy, np.:

Math.sin(x / 32.0 - time * 0.5)

3. Zmie艅 schemat kolor贸w

Eksperymentuj z r贸偶nymi przesuni臋ciami fazowymi lub u偶yj innych funkcji do mapowania kolor贸w.

4. Zmie艅 pr臋dko艣膰 animacji

Pomn贸偶 time przez wsp贸艂czynnik, aby przyspieszy膰 lub spowolni膰 animacj臋:

time = time / 1000 * 0.5; // Spowolnienie o po艂ow臋
馃挕 Optymalizacja: Dla lepszej wydajno艣ci mo偶esz pre-kalkulowa膰 warto艣ci lub u偶ywa膰 mniejszego rozmiaru canvas z p贸藕niej skalowaniem CSS.
Inne samouczki:

Fraktale - 25 linii JavaScript

Sinus scroll - 30 linii

Gra "Angry Chickens"

Animowane krzywe kwadratowe - 40 linii

Animowane konstelacje cz膮steczek - 42 linie

Eksperyment z enginem fizyki