Regelmäßigkeit im Chaos - ein Canvas-Effekt mit Faszinations-Garantie

Regelmäßigkeit im Chaos - ein Canvas-Effekt mit Faszinations-Garantie
Von Lars Ebert am 22.09.14, 11:00
Kategorien: Design, Inspiration, JavaScript, Programmieren and Tutorials

In meinem letzten Artikel habe ich beschrieben, wie man mit ein wenig Physik, JavaScript und dem Canvas einen interaktiven Effekt erstellen kann. In diesem Artikel möchte ich einen neuen Effekt vorstellen, der diesmal zwar nicht interaktiv, aber nicht weniger interessant ist.

Zuerst das Resultat

Ein bisschen Theorie über Zykloide

Auch in diesem Effekt brauchen wir ein bisschen Theorie, jedoch nicht sehr viel. Obwohl die Bewegungen beinahe chaotisch wirken, steckt hinter ihnen ein mathematisches Muster. Punkt für Punkt wird das Muster aus der Rotation von Kreisen gegeneinander generiert. Klingt kompliziert, ist aber eigentlich ganz einfach. Gehen wir es Schritt für Schritt durch.

Als erstes denken wir uns einen Kreis in der Mitte. Dieser Kreis rotiert pro Iteration um einen bestimmten Winkel, zum Beispiel 2°.

Nun denken wir uns einen zweiten Kreis, der an dem Punkt auf dem ersten Kreis befestigt ist. Dieser Kreis ist halb so groß und rotiert doppelt so schnell, jedoch in die entgegengesetzte Richtung.

Wir können nun beliebig viele Kreise hinzufügen, so wird die entstehende Bewegung immer komplexer. So sieht die Bewegung zum Beispiel mit drei Kreisen aus.

Umsetzung I: HTML

Bevor wir den Effekt mit JavaScript umsetzen müssen, brauchen wir zunächst ein HTML-Dokument mit einem Canvas. Hier binden wir auch die JavaScript-Datei ein.

<!DOCTYPE html>
<html>

<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	
	<title>Cycloids</title>
</head>

<body>
	<canvas></canvas>
	<script type="text/javascript" src="js/main.js"></script>
</body>

</html>

Umsetzung II: Javascript

Nun können wir mit dem Erstellen des Effekts beginnen. Als erstes definieren wir ein paar Konstanten.

var width = 600, height = 600;
var numCircles = 5;
var ratio = 1.41;
var maxRadius = 290;
var step = Math.PI / 180;

width und height stellen die Größe des Canvas dar. numCircles definiert die Anzahl der Kreise, ratio ist das Verhältnis zwischen den Größen der einzelnen Kreise. In diesem Fall ist jeder Kreis 1,41 mal so klein wie der vorherige. maxRadius ist der maximale gesamte Radius aller Kreise. So passt das Muster immer in den Canvas. step ist der Winkel, um den sich der erste Kreis jede Iteration dreht.

document.body.setAttribute('style', 'margin: 0px; padding: 0px; background: #111111; text-align: center;');

var canvas = document.getElementsByTagName('canvas')[0];

canvas.setAttribute('style', 'margin: 0px auto;');
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);

var context = canvas.getContext('2d');

Nun bereiten wir den Canvas vor. Zunächst weisen wir dem Body ein paar Stile vor, danach wählen wir den Canvas aus und geben ihm auch Stile, Breite und Höhe. Zuletzt fragen wir noch den Context ab.

var smallestRadius = maxRadius;
while(smallestRadius > .01 && accumulatedRadius(smallestRadius) > maxRadius) {
	smallestRadius -= .01;
}

Als nächstes ermitteln wir den Radius des kleinsten Kreises. Insgesamt dürfen alle Radien zusammen nicht größer sein als maxRadius. Also starten wir mit maxRadius und verkleinern den Radius so lange, bis alle Radien zusammen kleiner als maxRadius sind. Mit der Funktion accumulatedRadius berechnen wir den summierten Radius aller Kreise.

function accumulatedRadius(smallestRadius) {
	var result = 0;
	for(var i = 0; i < numCircles; i++) {
		result += smallestRadius;
		smallestRadius *= ratio;
	}
	return result;
}

In der Funktion accumulatedRadius gehen wir einfach alle Kreise durch und addieren den Radius zur Summe.

Nachdem wir nun den kleinsten Radius kennen, können wir die Kreise generieren und in einem Array speichern.

var circles = [];
for(var i = 0; i < numCircles; i++) {
	circles.push({
		angle: 0,
		radius: smallestRadius * Math.pow(ratio, numCircles - i - 1)
	});
}

Für jeden Kreis speichern wir im Array circles ein Objekt, dass den Winkel und Radius dieses Kreises enthält. Der Winkel ist zu Anfang bei allen Kreisen 0. Den Radius können wir aus dem kleinsten Radius generieren, indem wir pro Kreis den Radius mit ratio multiplizieren.

context.fillStyle = '#ffffff';
window.setInterval(function() {
	var position = {
		x: width / 2,
		y: height / 2
	};
	var deltaAngle = step;
	for(var i = 0; i < numCircles; i++) {
		position.x += circles[i].radius * Math.sin(circles[i].angle);
		position.y += circles[i].radius * Math.cos(circles[i].angle);
		circles[i].angle += deltaAngle;
		deltaAngle *= -1 * ratio;
	}
	context.fillRect(position.x, position.y, 1, 1);
}, 1);

Schließlich können wir die Iteration starten. Den fillStyle setzen wir auf #ffffff.

In jeder Iteration beginnen wir im Mittelpunkt des Canvas. Diesen speichern wir im Objekt position. In der Variable deltaAngle speichern wir den Winkel, um den der erste Kreis rotieren wird. Nun gehen wir alle Kreise durch.

Mit Sinus und Cosinus verschieben wir für jeden Kreis position. So berechnen wir in jeder Iteration den Endpunkt der aktuellen Kreiswinkel. Diesen Endpunkt zeichnen wir schließlich auf unserem Canvas ein. Nach jeder Iteration werden die für die Kreise gespeicherten Winkel um deltaAngle geändert, wobei deltaAngle selbst mit jedem Kreis wieder umgekehrt und mit ratio multipliziert wird, genau wie vorhin bei unseren drei Kreisen.

So entsteht Punkt für Punkt ein komplexes Muster. Obwohl dieses Muster zufällig wirkt, zeichnen sich mit der Zeit Regelmäßigkeiten ab. So oder so ist der Effekt jedenfalls interessant.

Variationen und Spielerei

Den Effekt selbst kann man über die Anzahl der Kreise und die Größenverhältnisse variieren. Dafür habe ich hier eine interaktive Demo hochgeladen.

Mit diesem Effekt zeige ich nur stellvertretend die vielen Möglichkeiten, die uns dank Canvas, JavaScript und ein bisschen Fantasie offen stehen. Hiermit fordere ich Dich auf, ein wenig mit dem Canvas herum zu experimentieren. Im Web gibt es schon extrem viele Canvas-Experimente, aber der Kreativität sind keine Grenzen gesetzt.