Für diese Seite benötigte ich einige Zahnräder, welche ich als Logo und als Hintergrund verwendete. Die Zahnräder sollten sich bei bedarf drehen und dynamisch erzeugt werden. 

Was wir benötigen ist erst einmal ein Zahnrad welches folgende Eigenschaften hat:

  • Position (x, y),
  • Modulo (m),
  • Zähnezahl (z),
  • Farbe (color_gear),
  • Drehung (angle),
  • Rotationsgeschwindigkeit (speed),
  • Richtung (dir).

 Übersetzt in eine Klasse sieht das dann folgendermaßen aus:

/* Class Gear */
function Gear(x, y, m, z, color_gear, start_angle, speed, dir) {
    this.angle = start_angle;		// Start angle
    this.speed = speed;			// Speed
    this.dir = dir;             	// Direction
    this.x = x;                 	// Position X
    this.y = y;                 	// Position Y
    this.m = m;                 	// Modulo
    this.z = z;                 	// Teeth Number
    this.color_gear = color_gear;	// Color
    if(this.angle == 0 && dir == true) {// Zentrieren des Zahnrads
        this.angle = Math.PI/this.z;
    }
}

Dabei kann jede Funktion als Klasse fungieren. Mit Hilfe von prototype werden Getter und Setter zu der Klasse hinzugefügt:

/* Getter and Setter for the Class Gear */
Gear.prototype.getX = function() { return this.x; }
Gear.prototype.getY = function() { return this.y; }
Gear.prototype.getModulo = function() { return this.m; }
Gear.prototype.getTeeth = function() { return this.z; }
Gear.prototype.getColor = function() { return this.color_gear; }
Gear.prototype.getAngle = function() { return this.angle; }
Gear.prototype.setAngle = function(a) { return this.angle = a; }
Gear.prototype.getDirection = function() { return this.dir; }
Gear.prototype.getSpeed = function() { return this.speed; }

Nun besteht ein Getriebe nicht nur aus einem Zahnrad sondern aus mehreren, deshalb wird eine Getriebe Klasse benötigt. Welche auch die Zahnräder zeichnet und animiert. Diese Klasse hat folgende Eigenschaften:

  • Array von Zahnräder (allGears),
  • Context des Canvas Elements (ctx),
  • Abmessungen des Canvas Elements (width, height).
/* Class Gears This Class holds the all the gears and methods to draw */
function Gears(canvas) {
    this.ctx = canvas.getContext("2d");;
    this.allGears = [];
    this.width = canvas.width;
    this.height = canvas.height;
    this.timer;
    var _this = this;

    // Funktionen..
}

 Als Funktionen benötigen wir:

  • eine zum zeichnen der Zahnräder (drawGear),
  • zum zeichnen eines Zahnrades (draw),
  • zum löschen der Zeichenfläche (clear),
  • Hinzufügen eines Zahnrades (AddGear),
  • Die Animation starten (StartAnimation),
  • Die Zahnräder nur Statisch zeichnen (DrawStatic),
  • Die Animation wieder zu stoppen (StopAnimation).

Die Öffentlichen Funktion, welche als Benutzer zur Verfügung stehen sind schnell geschrieben.

Gears.prototype.AddGear = function(gear) {
    this.allGears.push(gear);
}
Gears.prototype.StartAnimation = function() {
    this.timer = setInterval(function() {_this.draw();}, 25);
} Gears.prototype.DrawStatic = function() { this.draw(); } Gears.prototype.StopAnimation = function() { clearTimeout(this.timer); }

Es fehlen noch draw() und clear():

this.clear = function() {
    this.ctx.clearRect(0 ,0 ,this.width, this.height);
}

this.draw = function() {
    this.clear();
        
    for (var i = 0; i < this.allGears.length; i++) {
        var g = this.allGears[i];
        this.drawGear(g);
    }
}

Und zuletzt noch die drawGear():

this.drawGear = function(gear) {
    var z = gear.getTeeth();
    var m = gear.getModulo();
    var df = (z - 2.5) * m;	// Innenradius
    var d = z * m;		// Radius
    var dk = (z + 2) * m;	// Außenradius
    var tooth_angle = Math.PI/ z;
    var d_i_big = (z - 7) * m;
    var d_i_black = 8 * m;
    var rect_w = (z - 3) * m * 2;
    var rect_h = 6 * m;
    var x = gear.getX();
    var y = gear.getY();
    var angle = gear.getAngle();
    //Drehung neu berechnen
    if(gear.getDirection()) gear.setAngle(angle - gear.getSpeed()/40);
    else gear.setAngle(angle + gear.getSpeed()/40);
    angle = gear.getAngle();     
    this.ctx.beginPath();
    this.ctx.moveTo(x + Math.sin(angle - tooth_angle/1.5) * df, y - Math.cos(angle - tooth_angle/1.5) * df);
    for (var phi = angle; phi < angle + 2*Math.PI; phi = phi + 2*Math.PI/z) {
        //linke Seite des Zahns
        this.ctx.lineTo(x + Math.sin(phi - tooth_angle/4) * dk, y - Math.cos(phi - tooth_angle/4) * dk);
        //oberer Teil des Zahns
        this.ctx.arc(x, y, dk, phi - tooth_angle/4 - Math.PI / 2, phi + tooth_angle/4 - Math.PI / 2);
        //rechte Seite des Zahns
        this.ctx.lineTo(x + Math.sin(phi + tooth_angle/1.5) * df, y - Math.cos(phi + tooth_angle/1.5) * df);
        //Der untere Teil des Zahns
        this.ctx.arc(x, y, df, phi + tooth_angle/1.5 - Math.PI / 2, phi + 2 * tooth_angle/1.5 - Math.PI / 2);
    }
    //Das Innenloch ausschneiden
    this.ctx.moveTo(x+d_i_big, y);
    this.ctx.arc(x, y, d_i_big, 0, 2*Math.PI, true);
    //this.ctx.closePath();
    this.ctx.fillStyle=gear.getColor();
    this.ctx.fill();      
    //Das Fleisch um das Innenloch
    this.ctx.fillStyle=gear.getColor();
    this.ctx.arc(x, y, d_i_black, 0, 2*Math.PI, false);
    this.ctx.arc(x, y, 2.5*m, 2*Math.PI, 0, true);        
    this.ctx.closePath();
    this.ctx.fill();
    //die Speichen des Zahnrads
    this.ctx.beginPath();
    this.ctx.translate(x, y);
    this.ctx.rotate(angle);
    this.ctx.fillStyle=gear.getColor();
    this.ctx.fillRect(-rect_w/2, -rect_h/2, rect_w, rect_h);
    this.ctx.rotate(Math.PI / 2);
    this.ctx.fillStyle=gear.getColor();
    this.ctx.fillRect(-rect_w/2, -rect_h/2, rect_w, rect_h);
    // zurück rotieren
    //this.ctx.beginPath();
    this.ctx.rotate(-angle);   
    this.ctx.rotate(-Math.PI / 2);
    this.ctx.translate(-x, -y); 
    this.ctx.globalCompositeOperation = "destination-out";
    //this.ctx.beginPath();
    this.ctx.fillStyle=gear.getColor();
    this.ctx.arc(x, y, 2.5*m, 2*Math.PI, 0, false);        
    this.ctx.closePath();
    this.ctx.fill();
    this.ctx.globalCompositeOperation = "source-over";
}

Und fertig ist der Javascript Teil. Für die Verwendung in Html ist im Folgenden ein kleines Beispiel gezeigt:

<!DOCTPYE html>
<html>
	<head>
		<title>Canvas Gears</title>
		<script language="javascript" type="text/javascript" src="Gears.js"></script>
	</head>
	<body>
		<canvas id="myCanvas" width="300" height="160" style="border:1px solid #000000; ">
		</canvas>
	</body>
	<script>
var speed = Math.PI / 16;
var m = 2;
var z1 = 36;
var z2 = 24;
var z3 = 12;

var canvas = document.getElementById('myCanvas');
if (canvas && canvas.getContext) {
    var ctx = canvas.getContext("2d");
    var gears = new Gears(canvas1);
    var g1 = new Gear((z1+2)*m, (z1+2)*m, m, z1, "rgba(0, 0, 0, 1)", 0, speed, false);
    var g2 = new Gear((z1+2)*m + (z1+z2)*m, (z1+2)*m, m, z2, "rgba(0, 0, 0, 1)", 0, speed * 1.5, true);
    var g3 = new Gear((z1+2)*m + (z1+z2)*m + (z2+z3)*m, (z1+2)*m, m, z3, "rgba(0, 0, 0, 1)", 0, speed * 3, false);
    gears.AddGear(g1);
    gears.AddGear(g2);
    gears.AddGear(g3);
    
    gears.StartAnimation();
}
</script>
</html>

Wie das ganze aussehen kann seht ihr im Logo dieser Seite und im Hintergrund.