Schöner Code ist eine Freude zu schreiben, aber es ist schwierig, diese Freude mit anderen Programmierern zu teilen, ganz zu schweigen von Nicht-Programmierern. In meiner Freizeit zwischen meinem Beruf und meiner Familie habe ich mit der Idee eines Programmiergedichts gespielt, indem ich das Canvas-Element zum Zeichnen im Browser verwende. Es gibt eine Vielzahl von Begriffen, um visuelle Experimente am Computer zu beschreiben, wie zum Beispiel Dev-Art, Code-Skizze, Demo und interaktive Kunst, aber letztendlich habe ich mich darauf festgelegt , Gedichte zu programmieren , um diesen Prozess zu beschreiben. Die Idee hinter einem Gedicht ist eine polierte Prosa, die leicht zu teilen, prägnant und ästhetisch ist. Es ist keine halbfertige Idee in einem Skizzenbuch, sondern ein zusammenhängendes Stück, das dem Betrachter zum Vergnügen präsentiert wird. Ein Gedicht ist kein Werkzeug, sondern existiert, um eine Emotion zu evozieren.

Zu meinem eigenen Vergnügen lese ich Bücher über Mathematik, Rechnen, Physik und Biologie. Ich habe sehr schnell gelernt, dass wenn ich über eine Idee spreche, die Leute ziemlich schnell langweilt. Visuell kann ich einige dieser Ideen, die ich faszinierend finde, aufgreifen und jedem schnell ein Gefühl der Verwunderung vermitteln, auch wenn sie die Theorie hinter dem Code und den Konzepten, die ihn steuern, nicht verstehen. Man braucht keine harte Philosophie oder Mathematik, um ein Programmiergedicht zu schreiben, sondern nur den Wunsch, etwas live zu sehen und auf dem Bildschirm zu atmen.

Der Code und die Beispiele, die ich unten zusammengestellt habe, werden ein Verständnis dafür schaffen, wie man diesen schnellen und höchst befriedigenden Prozess tatsächlich abwickeln kann. Wenn Sie dem Code folgen möchten, können Sie das tun Laden Sie die Quelldateien hier herunter.

Der Haupttrick bei der Erstellung eines Gedichtes besteht darin, es leicht und einfach zu halten. Verbring nicht drei Monate damit, eine wirklich coole Demo zu erstellen. Erstellen Sie stattdessen 10 Gedichte, die eine Idee entwickeln. Schreiben Sie experimentellen Code, der aufregend ist, und haben Sie keine Angst zu versagen.

Einführung in Canvas

Für einen schnellen Überblick ist die Zeichenfläche im Wesentlichen ein 2D-Bitmap-Bildelement, das sich im DOM befindet, auf das gezeichnet werden kann. Das Zeichnen kann entweder mit einem 2d-Kontext oder einem WebGL-Kontext erfolgen. Der Kontext ist das JavaScript-Objekt, mit dem Sie auf die Zeichenwerkzeuge zugreifen können. Die JavaScript-Ereignisse, die für Canvas verfügbar sind, sind im Gegensatz zu denen, die für SVG verfügbar sind, sehr Barebones. Jedes Ereignis, das ausgelöst wird, gilt für das Element als Ganzes, nicht für alles, was wie ein normales Bildelement auf die Leinwand gezeichnet wird. Hier ist ein einfaches Leinwandbeispiel:

var canvas = document.getElementById('example-canvas');var context = canvas.getContext('2d');//Draw a blue rectanglecontext.fillStyle = '#91C0FF';context.fillRect(100, // x100, // y400, // width200 // height);//Draw some textcontext.fillStyle = '#333';context.font = "18px Helvetica, Arial";context.textAlign = 'center';context.fillText("The wonderful world of canvas", // text300, // x200 // y);

Es ist ziemlich einfach, anzufangen. Das einzige, was ein wenig verwirrend sein kann, ist, dass der Kontext mit den Einstellungen wie fillStyle, lineWidth, font und strokeStyle konfiguriert werden muss, bevor der eigentliche Zeichenaufruf verwendet wird. Es ist leicht zu vergessen, diese Einstellungen zu aktualisieren oder zurückzusetzen und einige unbeabsichtigte Ergebnisse zu erhalten.

Dinge bewegen

Das erste Beispiel wurde nur einmal ausgeführt und zeichnete ein statisches Bild auf die Arbeitsfläche. Das ist in Ordnung, aber wenn es wirklich Spaß macht, ist es bei 60 Bildern pro Sekunde aktualisiert. Moderne Browser haben die integrierte Funktion requestAnimationFrame , die benutzerdefinierten Zeichnungscode mit den Zeichenzyklen des Browsers synchronisiert. Dies hilft in Bezug auf Effizienz und Laufruhe. Das Ziel einer Visualisierung sollte Code sein, der mit 60 Bildern pro Sekunde summt.

(Ein Hinweis zur Unterstützung: Es gibt einige einfache Polyfills, wenn Sie ältere Browser unterstützen müssen.)

var canvas = document.getElementById('example-canvas');var context = canvas.getContext('2d');var counter = 0;var rectWidth = 40;var rectHeight = 40;var xMovement;//Place rectangle in the middle of the screenvar y = ( canvas.height / 2 ) - ( rectHeight / 2 );context.fillStyle = '#91C0FF';function draw() {//There are smarter ways to increment time, but this is for demonstration purposescounter++;//Cool math below. More explanation in the text following the code.xMovement = Math.sin(counter / 25) * canvas.width * 0.4 + canvas.width / 2 - rectWidth / 2;//Clear the previous drawing resultscontext.clearRect(0, 0, canvas.width, canvas.height);//Actually draw on the canvascontext.fillRect(xMovement,y,rectWidth,rectHeight);//Request once a new animation frame is available to call this function againrequestAnimationFrame( draw );}draw();

Es ist schön, dass der Code etwas mehr interne Struktur hat, aber es macht nicht wirklich viel interessanteres. Hier kommt eine Schleife ins Spiel . Im Szenenobjekt erstellen wir ein neues DotManager- Objekt. Es ist praktisch, diese Funktionalität in einem separaten Objekt zu sammeln, da es einfacher und sauberer ist, wenn mehr und mehr Komplexität zur Simulation hinzugefügt wird.

var DotManager = function( numberOfDots, scene ) {this.dots = [];this.numberOfDots = numberOfDots;this.scene = scene;for(var i=0; i < numberOfDots; i++) {this.dots.push( new Dot(Math.random() * this.canvas.width,Math.random() * this.canvas.height,this.scene));}};DotManager.prototype = {update : function( dt ) {for(var i=0; i < this.numberOfDots; i++) {this.dots[i].update( dt );}}};

Anstatt einen Punkt zu erstellen und zu aktualisieren, erstellen und aktualisieren wir jetzt den DotManager . Wir werden 5000 Punkte erstellen, um loszulegen.

function Scene() {...this.dotManager = new DotManager(5000, this);...};Scene.prototype = {...update : function( dt ) {this.dotManager.update( dt );}...};

Nehmen Sie für jeden neu erstellten Punkt die ursprüngliche Position ein und legen Sie den Farbton auf der Breite der Leinwand fest. Die Utils.hslToFillStyle- Funktion ist eine kleine Hilfsfunktion, die ich hinzugefügt habe, um einige Eingabevariablen in die korrekt formatierte fillStyle- Zeichenfolge zu transformieren. Schon jetzt sehen die Dinge spannender aus. Die Punkte verschmelzen schließlich miteinander und verlieren ihren Regenbogeneffekt, nachdem sie Zeit haben sich zu verteilen. Wiederum ist dies ein Beispiel für das Fahren von Visuals mit ein wenig Mathematik oder variablen Eingaben. Ich genieße wirklich Farben mit dem HSL-Farbmodell mit generativer Kunst anstelle von RGB aufgrund der Benutzerfreundlichkeit. RGB ist ein wenig abstrakt.

Benutzerinteraktion mit einer Maus

Bisher gab es keine echte Benutzerinteraktion.

var Mouse = function( scene ) {this.scene = scene;this.position = new THREE.Vector2(-10000, -10000);$(window).mousemove( this.onMouseMove.bind(this) );};Mouse.prototype = {onMouseMove : function(e) {if(typeof(e.pageX) == "number") {this.position.x = e.pageX;this.position.y = e.pageY;} else {this.position.x = -100000;this.position.y = -100000;}}};

Dieses einfache Objekt kapselt die Logik der Mausaktualisierungen vom Rest der Szene ein. Es aktualisiert nur den Positionsvektor bei einer Mausbewegung. Die restlichen Objekte können dann vom Positionsvektor der Maus abtasten, wenn ihnen ein Verweis auf das Objekt übergeben wird. Eine Einschränkung, die ich hier ignoriere, ist, wenn die Breite der Leinwand nicht eins zu eins mit den Pixel-Dimensionen des DOM ist, dh eine ansprechend skalierte Leinwand oder eine höhere Pixeldichte (Retina) Leinwand oder wenn die Leinwand nicht an der oben links. Die Koordinaten der Maus müssen entsprechend angepasst werden.

var Scene = function() {...this.mouse = new Mouse( this );...};

Das einzige, was für die Maus übrig blieb, war das Mausobjekt in der Szene zu erstellen. Jetzt, da wir eine Maus haben, locken wir die Punkte dazu.

function Dot( x, y, scene ) {...this.attractSpeed = 1000 * Math.random() + 500;this.attractDistance = (150 * Math.random()) + 180;...}

Ich fügte dem Punkt einige skalare Werte hinzu, so dass sich jeder in der Simulation ein wenig anders verhält, um ihm ein wenig Realismus zu geben. Spielen Sie mit diesen Werten herum, um ein anderes Gefühl zu bekommen. Nun zu der Attract-Maus-Methode. Es ist ein bisschen lang mit den Kommentaren.

attractMouse : function() {//Again, create some private variables for this methodvar vectorToMouse = new THREE.Vector2(),vectorToMove = new THREE.Vector2();//This is the actual public methodreturn function(dt) {var distanceToMouse, distanceToMove;//Get a vector that represents the x and y distance from the dot to the mouse//Check out the three.js documentation for more information on how these vectors workvectorToMouse.copy( this.scene.mouse.position ).sub( this.position );//Get the distance to the mouse from the vectordistanceToMouse = vectorToMouse.length();//Use the individual scalar values for the dot to adjust the distance movedmoveLength = dt * (this.attractDistance - distanceToMouse) / this.attractSpeed;//Only move the dot if it's being attractedif( moveLength > 0 ) {//Resize the vector to the mouse to the desired move lengthvectorToMove.copy( vectorToMouse ).divideScalar( distanceToMouse ).multiplyScalar( moveLength );//Go ahead and add it to the current position now, rather than in the draw callthis.position.add(vectorToMove);}};}()

Diese Methode könnte etwas verwirrend sein, wenn Sie nicht auf dem Laufenden sind. Vektoren können sehr visuell sein und können helfen, wenn Sie ein paar Skizzen auf einem mit Kaffee verschmutzten Stück Papier malen. Im einfachen Englisch erhält diese Funktion den Abstand zwischen der Maus und dem Punkt. Dann bewegt sich der Punkt etwas näher zum Punkt, je nachdem, wie nah es bereits dem Punkt und der verstrichenen Zeit ist. Dazu berechnet man die zu bewegende Entfernung (eine normale Skalarzahl) und multipliziert diese mit dem normalisierten Vektor (einem Vektor mit der Länge 1) des auf die Maus zeigenden Punkts. Ok, dieser letzte Satz war nicht unbedingt einfach Englisch, aber es ist ein Anfang.