Kaunis koodi on ilo kirjoittaa, mutta on vaikeaa jakaa sitä iloa muiden ohjelmoijien kanssa, puhumattakaan myös ei-ohjelmoijista. Oman työpäivän ja perheen välisenä aikana olen ollut leikkimässä ajatus ohjelmointi runosta käyttäen kangasta elementtiä piirrä selaimeen. Tässä on paljon termejä kuvaamaan visuaalisia kokeiluja tietokoneessa, kuten dev-taidetta, koodin luonnosta, demosta ja interaktiivisesta taidosta, mutta lopulta asettin ohjelmointimaailman kuvaamaan tätä prosessia. Runon idea on kiillotettu proosainen kappale, joka on helposti jaettavissa, ytimekäs ja esteettinen. Se ei ole puolivalmiita käsityksiä luonnoksessa, vaan yhtenäinen kappale näytetään katsojalle heidän nautinnostaan. Runo ei ole työkalu, vaan se on tunne herättää.

Omasta nautinnosta olen lukenut kirjoja matematiikasta, laskennasta, fysiikasta ja biologiasta. Olen oppinut todella nopeasti, että kun pudotan ajatukseen, se kyllästyy ihmisiin melko nopeasti. Nähtävästi voin ottaa joitain näistä ideoista, jotka ovat kiehtovia ja antavat nopeasti ihmisille ihmettekön, vaikka eivät ymmärrä koodin taustalla olevaa teoriaa ja käsitteitä, jotka ohjaavat sitä. Sinun ei tarvitse käsitellä mitään kovaa filosofiaa tai matematiikkaa kirjoittaa ohjelmoiva runo, vain halu nähdä jotain elää ja hengittää näytöllä.

Koodi ja esimerkit, jotka olen koonnut alla, auttavat ymmärtämään, miten todella poistetaan tämä nopea ja erittäin tyydyttävä prosessi. Jos haluat seurata koodia, voit lataa lähdetiedostot täältä.

Tärkein temppu, kun todella luoda runo on pitää se kevyt ja yksinkertainen. Älä vietä kolme kuukautta rakentaa todella hienoa demoa. Sen sijaan luo 10 runoa, jotka kehittävät ajatuksen. Kirjoita kokeellinen koodi, joka on jännittävä ja älä pelkää epäonnistua.

Canvas-esittely

Lyhyt katsaus, kangas on olennaisesti 2d bittikarttakuvan elementti, joka asuu DOM: ssa, joka voidaan piirtää. Piirustus voidaan tehdä joko 2d-kontekstilla tai WebGL-kontekstilla. Konteksti on JavaScript-objekti, jota käytät päästäksesi piirtotyökaluihin. JavaScript-tapahtumia, jotka ovat saatavilla kankaalle, ovat hyvin paljaita, toisin kuin SVG: lle käytettävissä olevat. Mikä tahansa tapahtuma, joka laukaistaan, on elementti kokonaisuudessaan, eikä mitään piirretty kankaalle, aivan kuin tavallinen kuvaelementti. Tässä on peruskangas esimerkki:

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

On melko yksinkertaista päästä alkuun. Ainoa asia, joka voi olla hieman sekava, on, että konteksti on määritettävä asetuksilla kuten fillStyle, lineWidth, font ja strokeStyle ennen varsinaista vetokutsua. On helppo unohtaa päivittää tai nollata nämä asetukset ja saada joitain tahattomia tuloksia.

Asioiden siirtäminen

Ensimmäinen esimerkki kesti vain kerran ja piirsi staattisen kuvan kankaalle. Se on OK, mutta kun se todella hauskaa on, kun se päivitetään 60 kuvaa sekunnissa. Nykyaikaisilla selaimilla on sisäänrakennettu toiminto requestAnimationFrame, joka synkronoi mukautetun piirroskoodin selaimen vetosykleihin . Tämä auttaa tehokkuutta ja sileyttä. Visualisoinnin kohteena tulisi olla koodi, joka häiritsee 60 kuvaa sekunnissa.

(Huomautus tuesta: käytettävissä on joitain yksinkertaisia ​​polyfilejä, jos tarvitset vanhempia selaimia.)

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

On hienoa, että koodi on hieman sisäistä rakennetta, mutta se ei todellakaan tee mitään, mikä on paljon mielenkiintoisempaa. Tällöin silmukka tulee sisään. Kun kohtausobjektissa luodaan uusi DotManager- objekti. On kätevää kerätä tämä toiminto erillisessä esineessä, koska se on helpompaa ja puhtaampaa, kun simulointiin lisätään entistä monimutkaisempaa.

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

Nyt kohtauksessa Dotin luomisen ja päivittämisen sijaan luomme ja päivitämme DotManagerin . Luomme 5000 pistettä aloittaaksemme.

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

Jokaiselle uudelle Dot-tiedostolle luodaan alkuperäinen sijainti ja asetetaan sen värisävy, missä se on kankaan leveydellä. Utils.hslToFillStyle- toiminto on pikku helper-funktio, jonka olen lisännyt muuttamaan tietyt tulomuuttujat oikein muotoiltuun fillStyle- merkkijonoon. Jo nyt asiat näyttävät jännittävämmiltä. Pisteet tulevat sulautumaan yhteen ja menettävät sateenkaaren vaikutuksen, kun heillä on aikaa hajottaa. Jälleen tämä on esimerkki ajo-grafiikoista, joissa on vähän matematiikkaa tai muuttuvia tuloja. Minulla on todella nautinto tehdä värejä HSL-värimallilla generatiivisen taiteen sijasta RGB: n sijaan helppokäyttöisyyden ansiosta. RGB on vähän abstrakti.

Käyttäjän vuorovaikutus hiirellä

Tähän mennessä ei ole ollut todellista käyttäjän vuorovaikutusta.

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

Tämä yksinkertainen kohde ympäröi hiiren päivitykset logiikasta muusta osasta. Se päivittää sijaintivektorin vain hiiren siirrolla. Loput esineet voivat sitten näyte hiiren sijaintivektorista, jos ne viedään viittauksen kohteeseen. Yksi huomautus, jonka jätän huomiotta täällä, on, jos kankaan leveys ei ole yksi, DOM: n pikselimittareilla eli resonoituneen kuvion tai suuremman pikselitiheyden (verkkokalvon) kankaalle tai jos kangas ei sijaitse ylävasen. Hiiren koordinaatteja on mukautettava vastaavasti.

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

Ainoa hiirelle jäänyt asia oli luoda hiiren kohde kohteen sisällä. Nyt kun meillä on hiiri, houkutellaan pisteitä siihen.

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

Lisäsin skalaarisia arvoja pisteeseen niin, että jokainen käyttäytyy hieman eri tavalla simuloinnissa ja antaa sille hieman realismia. Pelaa näitä arvoja, jotta saat erilaisen tunnelman. Nyt houkutella hiiren menetelmällä. Kommentteja on hieman kauan.

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

Tämä menetelmä voisi olla hieman sekava, jos et ole ajan tasalla vektori matematiikassa. Vektorit voivat olla hyvin visuaalisia, ja ne voivat auttaa, jos piirrät joitain scribbles ulos kahvia värjätään romu paperilla. Selkeänä englanniksi tämä toiminto saa hiiren ja pisteen välisen etäisyyden. Se siirtää pisteen hieman lähemmäksi pistettä sen mukaan, kuinka lähellä on jo piste ja kulunut aika. Se tekee tämän selvittämällä etäisyyden liikkua (normaali skalaariluku) ja kertomalla sitten hiirellä osoitetun pisteen normalisoituun vektoriin (vektori, jonka pituus on 1). Ok, tämä viimeinen lause ei välttämättä ole pelkkää englantia, mutta se on alku.