Beau code est une joie d'écrire, mais il est difficile de partager cette joie avec d'autres programmeurs, sans parler des non-programmeurs. Dans mon temps libre entre mon travail de jour et mon temps en famille, j'ai joué avec l'idée d'un poème de programmation utilisant l'élément canvas pour dessiner dans le navigateur. Il existe une multitude de termes décrivant des expériences visuelles sur ordinateur, telles que la dev art, le croquis de code, la démo et l' art interactif, mais j'ai finalement opté pour un poème de programmation pour décrire ce processus. L'idée derrière un poème est un morceau de prose poli, facile à partager, concis et esthétique. Ce n'est pas une idée à moitié terminée dans un carnet de croquis, mais une pièce cohérente présentée au spectateur pour son plaisir. Un poème n'est pas un outil, mais existe pour évoquer une émotion.

Pour mon plaisir, j'ai lu des livres sur les mathématiques, l'informatique, la physique et la biologie. J'ai appris très rapidement que lorsque je me promène sur une idée, cela ennuie assez rapidement les gens. Visuellement, je peux prendre certaines de ces idées que je trouve fascinantes et donner rapidement un sentiment d'émerveillement à toutes les personnes, même si elles ne comprennent pas la théorie qui sous-tend le code et les concepts. Vous n'avez pas besoin de vous soucier de la philosophie ou des mathématiques pour écrire un poème de programmation, juste un désir de voir quelque chose de vivant et de respirer à l'écran.

Le code et les exemples que j'ai rassemblés ci-dessous vous aideront à comprendre comment réaliser ce processus rapide et hautement satisfaisant. Si vous souhaitez suivre avec le code que vous pouvez Téléchargez les fichiers sources ici.

L'astuce principale lors de la création d'un poème est de le garder léger et simple. Ne passez pas trois mois à construire une démo vraiment cool. Au lieu de cela, créez 10 poèmes qui développent une idée. Ecrivez un code expérimental passionnant et n'ayez pas peur d'échouer.

Introduction à la toile

Pour un aperçu rapide, le canevas est essentiellement un élément d'image bitmap 2D qui réside dans le DOM pouvant être dessiné. Le dessin peut être fait en utilisant un contexte 2d ou un contexte WebGL. Le contexte est l'objet JavaScript que vous utilisez pour accéder aux outils de dessin. Les événements JavaScript disponibles pour les canevas sont très simples, contrairement à ceux disponibles pour le format SVG. Tout événement déclenché concerne l'élément dans son ensemble, et non quelque chose dessiné sur le canevas, tout comme un élément d'image normal. Voici un exemple de toile de base:

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);

C'est assez simple de commencer. La seule chose qui peut être un peu déroutante est que le contexte doit être configuré avec les paramètres tels que fillStyle, lineWidth, font, et strokeStyle avant que l'appel de dessin réel ne soit utilisé. Il est facile d'oublier de mettre à jour ou de réinitialiser ces paramètres et d'obtenir des résultats inattendus.

Faire bouger les choses

Le premier exemple ne fonctionnait qu'une seule fois et dessinait une image statique sur le canevas. C'est bien, mais quand ça devient vraiment amusant, c'est quand il est mis à jour à 60 images par seconde. Les navigateurs modernes ont la fonction intégrée requestAnimationFrame qui synchronise le code de dessin personnalisé avec les cycles de dessin du navigateur. Cela aide en termes d'efficacité et de douceur. La cible d'une visualisation doit être un code qui bourdonne à 60 images par seconde.

(Une note sur le support: il y a quelques polyfills simples disponibles si vous avez besoin de supporter les navigateurs plus anciens.)

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();

C'est bien qu'il y ait un peu plus de structure interne au code, mais ça ne fait pas vraiment beaucoup plus intéressant. C'est là qu'une boucle entre en jeu. Dans l'objet de la scène, nous allons créer un nouvel objet DotManager . Il est pratique de collecter cette fonctionnalité dans un objet distinct, car il est plus facile et plus propre de raisonner car de plus en plus de complexité est ajoutée à la simulation.

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 );}}};

Maintenant, dans la scène, plutôt que de créer et de mettre à jour un point , nous créons et mettons à jour le DotManager . Nous allons créer 5000 points pour commencer.

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

Pour chaque nouveau point créé, prenez sa position initiale et définissez sa teinte sur la largeur de la toile. La fonction Utils.hslToFillStyle est une petite fonction d'aide que j'ai ajoutée pour transformer certaines variables d'entrée dans la chaîne fillStyle correctement formatée. Déjà, les choses semblent plus excitantes. Les points finiront par fusionner et perdront leur effet arc-en-ciel après avoir eu le temps de se disperser. Encore une fois, ceci est un exemple de visuels de conduite avec un petit peu d’intrants mathématiques ou variables. J'aime beaucoup faire des couleurs avec le modèle couleur HSL avec l'art génératif plutôt que RVB en raison de la facilité d'utilisation. RGB est un petit résumé.

Interaction de l'utilisateur à l'aide d'une souris

Il n'y a pas eu d'interaction réelle avec les utilisateurs jusqu'à ce jour.

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;}}};

Cet objet simple encapsule la logique des mises à jour de la souris à partir du reste de la scène. Il ne met à jour que le vecteur de position lors d'un déplacement de la souris. Les autres objets peuvent alors échantillonner à partir du vecteur de position de la souris s'ils reçoivent une référence à l'objet. Une mise en garde que j'ignore ici est si la largeur du canevas n'est pas un à un avec les dimensions en pixels du DOM, c.-à-d. Une toile redimensionnée ou un canevas de rétine. en haut à gauche. Les coordonnées de la souris devront être ajustées en conséquence.

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

La seule chose qui restait à la souris était de créer l'objet souris à l'intérieur de la scène. Maintenant que nous avons une souris, attirons-les.

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

J'ai ajouté des valeurs scalaires au point afin que chacun se comporte un peu différemment dans la simulation pour lui donner un peu de réalisme. Jouez avec ces valeurs pour avoir une sensation différente. Maintenant, pour attirer la méthode de la souris. C'est un peu long avec les commentaires.

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);}};}()

Cette méthode peut être un peu déroutante si vous n'êtes pas à jour sur vos mathématiques vectorielles. Les vecteurs peuvent être très visuels et peuvent aider si vous dessinez des gribouillis sur un bout de papier taché de café. En anglais simple, cette fonction obtient la distance entre la souris et le point. Il déplace ensuite le point un peu plus près du point en fonction de sa proximité avec le point et du temps écoulé. Cela se fait en calculant la distance à déplacer (un nombre scalaire normal), puis en la multipliant par le vecteur normalisé (un vecteur de longueur 1) du point pointant vers la souris. Ok, cette dernière phrase n’était pas forcément l’anglais, mais c’est un début.