Krásný kód je radost psát, ale je obtížné sdílet tuto radost s ostatními programátory, nemluvě o programátorů. Ve volném čase mezi svou denní a rodinnou dobou jsem si hrál s myšlenkou programové básně s použitím elementu plátna pro kreslení prohlížeče. Tam je spousta termínů tam popisovat vizuální experimenty na počítači takový jak dev umění, skica kódu, demo, a interaktivní umění, ale nakonec jsem se usadil na programování básni popisovat tento proces. Myšlenka básně je leštěná próza, která je snadno sdílená, stručná a estetická. Nejde o napůl dokončenou myšlenku v náčrtu skic, ale souhrnný kus, který se divákovi představí pro jejich zábavu. Báseň není nástroj, ale existuje k vyvolání emocí.

Pro svůj vlastní zážitek jsem četl knihy o matematice, výpočtech, fyzice a biologii. Naučila jsem se opravdu rychle, že když vyrazím na nějakou myšlenku, lidem je dost rychle. Vizuálně můžu vzít některé z těchto myšlenek, které považuji za fascinující, a rychle dát někomu pocit zázraku, i když nerozumí teorii, která stojí za kódem a pojmy, které ho řídí. Nepotřebujete rukojeť na žádnou tvrdou filozofii ani matematiku, abyste napsali programovou báseň, jen touhu vidět něco živého a dýchat na obrazovce.

Kód a příklady, které jsem dal dohromady níže, začne chápat, jak vlastně vytáhnout tento rychlý a vysoce uspokojující proces. Pokud byste chtěli dodržovat kód, můžete zde stahujte zdrojové soubory.

Hlavním trikem při vytváření básně je udržet ji lehké a jednoduché. Neztrácejte tři měsíce a budujte jednu opravdu skvělou ukázku. Místo toho vytvořte 10 básní, které vyvíjejí nápad. Napište experimentální kód, který je vzrušující a nebojte se, aby selhal.

Úvod do plátna

Pro rychlý přehled je plátno v podstatě 2m bitmapový obrazový prvek, který žije v doméně DOM, na který lze vložit. Kreslení lze provést pomocí kontextu 2d nebo kontextu WebGL. Kontext je objekt JavaScript, který používáte k získání přístupu k nástrojům pro kreslení. Události jazyka JavaScript, které jsou k dispozici pro plátno, jsou velmi barebones, na rozdíl od těch, které jsou k dispozici pro SVG. Každá událost, která je spuštěna, je pro prvek jako celek, nikoli nic nakresleného na plátně, stejně jako normální obrazový prvek. Zde je příklad základního plátna:

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

Je to docela jednoduché začít. Jediná věc, která by mohla být trochu matoucí, je, že kontext musí být nakonfigurován pomocí nastavení jako fillStyle, lineWidth, font a strokeStyle před použitím skutečného volání. Je snadné zapomenout na aktualizace nebo obnovení těchto nastavení a získat některé neúmyslné výsledky.

Udělat věci se pohybují

První příklad běžel pouze jednou a nakreslil statický obraz na plátno. To je v pořádku, ale když se opravdu baví, je to, když je aktualizován rychlostí 60 snímků za sekundu. Moderní prohlížeče mají vestavěnou funkci requestAnimationFrame, která synchronizuje vlastní kreslicí kód s výkresovými cykly prohlížeče. To pomáhá z hlediska efektivity a hladkosti. Cílem vizualizace by mělo být kód, který bzučí po 60 snímcích za sekundu.

(Poznámka k podpoře: k dispozici jsou některé jednoduché polyfily, pokud potřebujete podporovat starší prohlížeče.)

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

To je hezké, že k kódu je trochu více vnitřní struktury, ale ve skutečnosti nic dělá mnohem zajímavější. To je místo, kde přichází smyčka. V objektu scény vytvoříme nový objekt DotManager . Je užitečné shromáždit tuto funkci v samostatném objektu, protože je to jednodušší a čistší na to, aby se do simulace přidala stále více a více složitosti.

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

Nyní ve scéně, než vytvoření a aktualizaci Dot , vytváříme a aktualizujeme DotManager . Vytvoříme 5 000 bodů, abychom mohli začít.

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

Pro každý vytvořený bod vytvořte jeho počáteční pozici a nastavte jeho odstín tam, kde je podél šířky plátna. Funkce Utils.hslToFillStyle je trochu pomocná funkce, kterou jsem přidal pro transformaci některých vstupních proměnných do správně formátovaného řetězce fillStyle . Už to vypadá více vzrušující. Tečky se nakonec spojují a ztratí svůj duhový efekt poté, co mají čas rozptýlit se. Opět je to příklad řidiče vizuálů s trochou matematických nebo variabilních vstupů. Jsem opravdu rád dělat barvy s HSL barevný model s generativním uměním spíše než RGB kvůli snadnému použití. RGB je trochu abstraktní.

Interakce uživatele pomocí myši

Do tohoto okamžiku nedošlo k žádné skutečné interakci uživatele.

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

Tento jednoduchý objekt zapouzdřuje logiku aktualizací myší ze zbytku scény. Aktualizuje pouze vektor polohy na pohyb myši. Zbývající objekty pak mohou vzorkovat z vektoru polohy myši, pokud jsou předány odkaz na objekt. Jedna poznámka, kterou zde ignoruji, je, pokud šířka plátna není jedna k jedné s dimenzemi pixelů DOM, tj. Plátna citlivě změněného plátna nebo plátna s vyšší hustotou pixelů (retina), nebo pokud plátno není umístěno na vlevo nahoře. Souřadnice myši budou muset být odpovídajícím způsobem upraveny.

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

Jediná věc, která zůstala pro myši, bylo vytvořit objekt myši uvnitř scény. Nyní, když máme myš, přitahujeme na ni tečky.

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

Přidala jsem do tečky nějaké skalární hodnoty, takže se každý z nich chová v simulaci trochu jinak, aby získal trochu realismu. Zahrajte si s těmito hodnotami, abyste získali jiný pocit. Nyní na metodu přitahování myší. Je to trochu dlouhá s komentáři.

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

Tato metoda by mohla být trochu matoucí, pokud nejste aktuální ve své vektorové matematice. Vektory mohou být velmi vizuální a mohou vám pomoct, pokud na kávu zbarvený šrot papíru nakreslíte nějaké čmáranice. V obyčejné angličtině tato funkce získává vzdálenost mezi myší a tečkou. To je pak přesunout tečku trochu blíže k bodu podle toho, jak blízko je to k bodu a čas, který uplynul. Dělá to tím, že zjistí vzdálenost, kterou se má pohybovat (normální skalární číslo), a pak ho vynásobíme normalizovaným vektorem (vektor s délkou 1) bodky směřující k myši. Ok, poslední věta nebyla nutně čistá angličtina, ale je to začátek.