Manuel Jasch

Alles rund um Internet & Technik

Ein Zahnrad in Javascript

Feb 182015

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.

  • Permalink
  • Kommentare

Latex Formeln im Web

Dez 062014

Für die Verwendung von Fomeln auf einer Website musste früher noch umständlich jede Formel über eine eigene Grafik eingebunden werden. Durch das mächtige Javascript ist das nun auch ohne Bilder möglich. MathJax bietet eine Bibliothek an die den Latex Code interpretieren kann und die Formel wie gewollt darstellen lässt.

Folgende Schritte sind bei der Verwendung zu beachten:

Ein minimal Beispiel könnte wie folgt aussehen (speichern unter index.html): 

<!DOCTYPE html>
<html lang="de">
<head>
<title>MathJax TeX Test</title>
<meta charset="utf-8"/>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
</head>
<body>
Wenn $a \ne 0$, dann existieren zwei Lösungen für \(ax^2 + bx + c = 0\) und diese sind
$$x_{1,2} = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
</body>
</html>

Im Beispiel sieht man die Verwendung von zwei Möglichkeiten wie im fließenden Text eine Formel eingebunden werden kann. Die abgesetzte Formel entsteht durch Verwendung des doppelten Dollar Zeichens. Wenn die Datei nun mit einem Browser geöffnet wird sollte dabei folgende Ausgabe in dem Browser Fenster erscheinen.

Wenn $a \ne 0$, dann existieren zwei Lösungen für \(ax^2 + bx + c = 0\) und diese sind
$$x_{1,2} = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

Für andere Personen kann der Latex Code sehr einfach über das Context Menü erreicht werden. Unter "Show Math As"->"MathML Code" kann der Code kopiert werden und in Word als Formel eingefügt werden. Bei "Show Math As"->"Tex Commands" wird der LaTex Code angezeigt.

  • Permalink
  • Kommentare

Eine Font für alle Bilder

Dez 062014

Im Webdesign werden viele kleine Icons verwendet. Sie müssen umständlich eingebunden werden und kosten den Entwickler viel Zeit beim anpassen. Wenn ein Image für einen Button gezeichnet werden soll muss dieser in unterschiedlichen Farbstufen vorliegen. Eine Font zu verwenden in der all diese Icons als Buchstabe oder Zahl hinterlegt sind wäre die elegante Lösung.

Vorteile

  • Bilder im Fließtext möglich (durch <span> Umgebung)
  • unterschiedliche Farben
  • einfach konfiguriert
  • alle wichtigen Bilder in einer Font

Nachteile

  • Unterstützung alter Webbrowser

Ich habe mal eine Font mit FontLab Studio erstellt und dabei häufig vorkommende Icons verwendet.

A B C D E F G H I J K L M
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m
a b c d e f g h i j k l m
n o p q r s t u v w x y z
n o p q r s t u v w x y z

Ein minimal Beispiel könnte wie folgt aussehen: Html Dokument (speichern unter index.html):

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8"/>
<link href="style.css" rel="stylesheet"/>
</head>
<body>
Das ist normaler Text, mit <span class="font_maja">l</span> einem Bild.
</body>
</html>

CSS Dokument (speichern unter style.css):

@font-face {
   font-family: 'font_maja';
   src: url(font_maja.eot);
   src: url('font_maja?#iefix') format('embedded-opentype'),  /* IE6-IE8 */
      url('font_maja.woff') format('woff'),	/* Moderne Browser */
      url('font_maja.ttf') format('truetype'),	/* Safari, Android, iOS */
      url('font_maja.svg#svgFontName') format('svg'); /* Ältere iOS-Geräte */
}
.font_maja { font-family: "font_maja"; }

Für den Fall-back für ältere Browser müssen unterschiedliche Formate bereitgestellt werden und dem Browser die möglichkeit gegeben werden sein Format auszuwählen.

Jetzt fehlen nur noch die Fonts unter dem Namen font_maja.ttf, im gleichen Ordner abzuspeichern, Fertig!.

Es gibt noch Kleinigkeiten die Optimiert werden könnten:

  • WOFF als Export (nicht gefunden unter FontLab Studio)
  • Restliche ASCII mit Icons befüllen
  • Font für andere Zwecke erstellen (z.B.: Muster, Elektrische Symbole, ..)

Nachtrag

Icomoon scheint schon so etwas professioneller zu machen: icomoon

  • Permalink
  • Kommentare
k