Matrix Regen – JavaScript Canvas Animation

Der Matrix Regen Effekt ist eine beliebte visuelle Darstellung, die durch den Science-Fiction-Film „Matrix“ berühmt wurde. Er zeigt fallende grüne Zeichen (Symbole) auf einem schwarzen Hintergrund und erzeugt so den Eindruck eines digitalen Regens. Dieser Effekt ist ein ikonisches Beispiel für die Darstellung von Computercode oder einer virtuellen Welt in der Popkultur.

Matrix Regen im Film

Der Matrix Regen Effekt wurde erstmals im Jahr 1999 im Film „Matrix“ von den Wachowski-Geschwistern verwendet. In dieser Filmszene wird der Protagonist Neo in eine virtuelle Welt gebracht, die als „Matrix“ bekannt ist. Dort sieht er fallende grüne Zeichen, die den Code der simulierten Realität darstellen. Dieser Effekt hat sich in der Popkultur fest etabliert und wird häufig verwendet, um eine futuristische, technologische oder virtuelle Atmosphäre zu vermitteln.

Folgendes Video zeigt den Anfang des „Matrix“ films und nach 30 Sekunden auch die Matrix Regen Effekt mit Schriftzeichen.

Matrix Regen als Canvas Animation

Der Matrix Regen Effekt wird in der Regel auf einem Computerbildschirm oder einem Leinwandprojektor angezeigt. Die fallenden grünen Zeichen werden durch eine zufällige Auswahl aus einem begrenzten Satz von Unicode-Zeichen erzeugt. Die Zeichen fallen vertikal von oben nach unten und erscheinen in unregelmäßigen Abständen und Geschwindigkeiten, um den Eindruck eines Regens oder Schneesturms zu erzeugen.

JavaScript Code für den Matrix Regen

Der folgende JavaScript-Code implementiert den Matrix Regen Effekt in einem HTML5 Canvas. Der Code nutzt Konzepte der objektorientierten Programmierung (OOP) und ES6 (ECMAScript 2015), um den Effekt zu erstellen und zu steuern. Er verwendet Klassen, um die verschiedenen Komponenten des Effekts zu organisieren, und ES6-Features wie private Methoden (#initialize, #resizeCanvas, #animate) und die class-Syntax.

class Symbol {
    constructor(x, y, fontSize, canvasHeight){
        this.characters = '0123456789';
        this.x = x;
        this.y = y;
        this.fontSize = fontSize;
        this.text = '';
        this.canvasHeight = canvasHeight;
    }
    draw(ctx){
        this.text = this.characters.charAt( Math.floor( Math.random() * this.characters.length ) );
        ctx.fillText(this.text, this.x * this.fontSize, this.y * this.fontSize);
        if( this.y * this.fontSize > this.canvasHeight && Math.random() > 0.95 ){
            this.y = 0;
        } else {
            this.y += 1;
        }
    }
}

class MatrixRain {
    constructor(canvasWidth, canvasHeight){
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        this.fontSize = 24;
        this.columns = this.canvasWidth / this.fontSize;
        this.symbols = [];
        this.#initialize();
    }
    #initialize(){
        for(let i = 0; i < this.columns; i++){
            this.symbols[i] = new Symbol(i, this.canvasHeight / this.fontSize +  this.fontSize , this.fontSize, this.canvasHeight );
        }
    }
    resize(canvasWidth, canvasHeight){
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        this.columns = this.canvasWidth / this.fontSize
        this.symbols = [];
        this.#initialize();
    }
}

class Timer {
    constructor(fps) {
        this.fps = fps;
        this.lastTime = 0; // last Update
        this.deltaTime = 0; // difference between last and current
        this.interval = 1000 / this.fps; // next Frame
    }
    update(currentTime) {
        this.deltaTime = currentTime - this.lastTime;
    }
    shouldUpdate() {
        return this.deltaTime > this.interval; // true if difference between last and current > interval
    }
    reset(currentTime) {
        this.lastTime = currentTime;
    }
}

class Render {
    constructor() {
        this.canvas = document.getElementById('canvas');
        this.ctx = this.canvas.getContext("2d");
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
    
        this.matrixrain = new MatrixRain(this.canvas.width, this.canvas.height);
        this.fps = 20;
        this.timer = new Timer(this.fps);
    
        window.addEventListener('resize', () => this.#resizeCanvas());
    }
    #resizeCanvas() {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
        this.matrixrain.resize(this.canvas.width, this.canvas.height);
    }
    startAnimation() {
        this.#animate(0);
    }
    #animate(currentTime) {
        this.timer.update(currentTime);
        if (this.timer.shouldUpdate()) {
            this.ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; // overlay each frame with transparent black to fade out
            this.ctx.textAlign = 'center';
            this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
            this.ctx.fillStyle = '#0aff0a';
            this.ctx.font = this.matrixrain.fontSize + 'px monospace';
            this.matrixrain.symbols.forEach(symbol => symbol.draw(this.ctx));
            this.timer.reset(currentTime);
        }
        requestAnimationFrame((timeStamp) => this.#animate(timeStamp));
    }
}
  
window.onload = () => {
    const render = new Render();
    render.startAnimation();
};
  1. Symbol-Klasse: Diese Klasse repräsentiert ein einzelnes fallendes Zeichen (Symbol) im Effekt. Sie enthält die Eigenschaften x und y für die Position, fontSize für die Schriftgröße, text für den aktuell angezeigten Zeichencode und canvasHeight für die Höhe des Canvas. Die Methode draw(ctx) wird verwendet, um das Symbol in den übergebenen ctx (Canvas-Kontext) zu zeichnen.
  2. MatrixRain-Klasse: Diese Klasse ist für die Verwaltung aller fallenden Symbole verantwortlich. Sie wird mit der Breite und Höhe des Canvas initialisiert und erstellt eine Reihe von Symbol-Instanzen, die in einem Array symbols gespeichert werden. Die private Methode #initialize wird verwendet, um die Symbole zu erstellen und zu platzieren. Die resize-Methode passt die Größe des Effekts an die Größe des Canvas an.
  3. Timer-Klasse: Diese Klasse steuert die Framerate (FPS) der Animation. Sie verfolgt die Zeit zwischen Frames und aktualisiert den Effekt nur, wenn eine bestimmte Zeitspanne (entsprechend der FPS) vergangen ist.
  4. Render-Klasse: Diese Klasse ist die zentrale Klasse, die das Canvas verwaltet, den Matrix Regen Effekt erstellt und die Animation startet. Sie initialisiert das Canvas, die MatrixRain– und Timer-Instanzen. Die privaten Methoden #resizeCanvas und #animate werden verwendet, um das Canvas bei einer Größenänderung anzupassen und die Animation zu steuern. Die öffentliche Methode startAnimation wird verwendet, um die Animation zu starten.

Der gesamte Code verwendet moderne JavaScript-Features wie Klassen, private Methoden und ES6-Syntax, um den Matrix Regen Effekt auf dem Canvas zu erzeugen und zu animieren. Es zeigt, wie mächtig und sauber der Code sein kann, wenn er in einem objektorientierten Stil geschrieben ist und ES6-Funktionen verwendet werden. Durch die Verwendung von Klassen wird der Code auch leichter zu warten und zu erweitern.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert