Ο όμορφος κώδικας είναι μια χαρά που γράφετε, αλλά είναι δύσκολο να μοιραστεί αυτή τη χαρά με άλλους προγραμματιστές, για να μην αναφέρουμε με τους μη προγραμματιστές. Στο ελεύθερο μου χρονικό διάστημα μεταξύ της καθημερινής μου δουλειάς και της οικογενειακής μου εποχής, έχω παίξει γύρω μου με την ιδέα ενός ποίηματος προγραμματισμού που χρησιμοποιεί το στοιχείο του καμβά για να σχεδιάσει το πρόγραμμα περιήγησης. Υπάρχουν πολλοί όροι εκεί έξω για να περιγράψουμε τα οπτικά πειράματα στον υπολογιστή, όπως το dev art, το sketch κώδικα, το demo και τη διαδραστική τέχνη, αλλά τελικά έχω εγκαταστήσει το ποίημα προγραμματισμού για να περιγράψω αυτή τη διαδικασία. Η ιδέα πίσω από ένα ποίημα είναι ένα στιλβωμένο κομμάτι της πεζογραφίας που είναι εύκολα κατανοητό, συνοπτικό και αισθητικό. Δεν είναι μια ημιτελής ιδέα σε ένα σκίτσο, αλλά ένα συνεκτικό κομμάτι που παρουσιάζεται στον θεατή για την απόλαυσή τους. Ένα ποίημα δεν είναι ένα εργαλείο, αλλά υπάρχει για να προκαλέσει ένα συναίσθημα.

Για τη δική μου απόλαυση διαβάζω βιβλία για τα μαθηματικά, τον υπολογισμό, τη φυσική και τη βιολογία. Έμαθα πολύ γρήγορα ότι όταν μετακομίζω σε μια ιδέα, οι άνθρωποι φεύγουν πολύ γρήγορα. Οπτικά μπορώ να πάρω μερικές από αυτές τις ιδέες που βρίσκω συναρπαστικές και να δώσω γρήγορα σε κάποιον μια αίσθηση απόλαυσης, ακόμη και αν δεν κατανοούν τη θεωρία πίσω από τον κώδικα και τις έννοιες που τον οδηγούν. Δεν χρειάζεστε μια λαβή σε οποιαδήποτε σκληρή φιλοσοφία ή μαθηματικά για να γράψετε ένα ποίημα προγραμματισμού, απλώς μια επιθυμία να δείτε κάτι ζωντανό και να αναπνεύσετε στην οθόνη.

Ο κώδικας και τα παραδείγματα που έχω συγκεντρώσει παρακάτω θα ξεκινήσουν μια κατανόηση για το πώς να βγάλουμε πραγματικά αυτή τη γρήγορη και εξαιρετικά ικανοποιητική διαδικασία. Εάν θέλετε να ακολουθήσετε μαζί με τον κώδικα που μπορείτε κατεβάστε εδώ τα αρχεία προέλευσης.

Το κύριο κόλπο όταν δημιουργείτε πραγματικά ένα ποίημα είναι να το κρατήσετε ελαφρύ και απλό. Μην περάσετε τρεις μήνες δημιουργώντας ένα πραγματικά δροσερό demo. Αντ 'αυτού, δημιουργήστε 10 ποιήματα που εξελίσσουν μια ιδέα. Γράψτε πειραματικό κώδικα που είναι συναρπαστικό και μην φοβάστε να αποτύχετε.

Εισαγωγή στον καμβά

Για μια γρήγορη επισκόπηση, ο καμβάς είναι ουσιαστικά ένα στοιχείο εικόνας 2d bitmap που ζει στο DOM που μπορεί να σχεδιαστεί. Το σχεδιάγραμμα μπορεί να γίνει χρησιμοποιώντας ένα περιβάλλον 2d ή ένα περιβάλλον WebGL. Το πλαίσιο είναι το αντικείμενο JavaScript που χρησιμοποιείτε για να αποκτήσετε πρόσβαση στα εργαλεία σχεδίασης. Τα συμβάντα JavaScript που είναι διαθέσιμα για καμβά είναι πολύ barebones, σε αντίθεση με αυτά που είναι διαθέσιμα για το SVG. Οποιοδήποτε γεγονός που ενεργοποιείται είναι για το στοιχείο στο σύνολό του και όχι για κάτι που έχει σχεδιαστεί στον καμβά, ακριβώς όπως ένα κανονικό στοιχείο εικόνας. Ακολουθεί ένα βασικό παράδειγμα καμβά:

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

Είναι πολύ εύκολο να ξεκινήσετε. Το μόνο πράγμα που θα μπορούσε να είναι λίγο συγκεχυμένο είναι ότι το πλαίσιο πρέπει να ρυθμιστεί με τις ρυθμίσεις όπως fillStyle, lineWidth, γραμματοσειρά και strokeStyle πριν χρησιμοποιηθεί η πραγματική κλήση κλήσης. Είναι εύκολο να ξεχάσετε να ενημερώσετε ή να επαναφέρετε αυτές τις ρυθμίσεις και να πάρετε κάποια ακούσια αποτελέσματα.

Κάνοντας τα πράγματα να κινούνται

Το πρώτο παράδειγμα έτρεξε μόνο μία φορά και εφάρμοσε μια στατική εικόνα στον καμβά. Αυτό είναι εντάξει, αλλά όταν πραγματικά διασκέδαση είναι όταν ενημερώνεται σε 60 καρέ ανά δευτερόλεπτο. Τα σύγχρονα προγράμματα περιήγησης έχουν την ενσωματωμένη λειτουργία requestAnimationFrame που συγχρονίζει τον προσαρμοσμένο κώδικα σχεδίασης με τους κύκλους κλήσης του προγράμματος περιήγησης. Αυτό βοηθά από την άποψη της αποδοτικότητας και της ομαλότητας. Ο στόχος μιας απεικόνισης θα πρέπει να είναι κώδικας που βουίζει μαζί στα 60 καρέ ανά δευτερόλεπτο.

(Σημείωση σχετικά με την υποστήριξη: υπάρχουν διαθέσιμες κάποιες απλές πολυχρηματοδοτήσεις, αν χρειαστεί να υποστηρίξετε παλαιότερα προγράμματα περιήγησης.)

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

Αυτό είναι ωραίο που υπάρχει λίγο περισσότερη εσωτερική δομή στον κώδικα, αλλά δεν κάνει πραγματικά τίποτα πολύ πιο ενδιαφέρον. Εκεί βρίσκεται ο βρόχος. Στο αντικείμενο σκηνής θα δημιουργήσουμε ένα νέο αντικείμενο DotManager . Είναι πρακτικό να συλλέγει αυτή τη λειτουργία σε ένα ξεχωριστό αντικείμενο, καθώς είναι πιο εύκολο και καθαρότερο να λογίζεται με όλο και περισσότερη πολυπλοκότητα προστίθεται στην προσομοίωση.

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

Τώρα στη σκηνή, αντί να δημιουργούμε και να ενημερώνουμε ένα Dot , δημιουργούμε και ενημερώνουμε το DotManager . Θα δημιουργήσουμε 5000 κουκίδες για να ξεκινήσετε.

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

Για κάθε νέο Dot που δημιουργείται, πάρτε την αρχική του θέση και ρυθμίστε την απόχρωση του σε εκεί που βρίσκεται κατά μήκος του πλάτους του καμβά. Η συνάρτηση Utils.hslToFillStyle είναι μια μικρή συνάρτηση helper που πρόσθεσα για να μετατρέψω κάποιες μεταβλητές εισόδου στη σωστά διαμορφωμένη συμβολοσειρά fillStyle . Ήδη τα πράγματα φαίνονται πιο συναρπαστικά. Οι τελείες τελικά θα συγχωνευθούν και θα χάσουν το φαινόμενο ουράνιου τόξου αφού έχουν χρόνο να διασκορπιστούν. Και πάλι, αυτό είναι ένα παράδειγμα οδήγησης οπτικών με λίγα μαθηματικά ή μεταβλητές εισόδους. Μου αρέσει πραγματικά να κάνω χρώματα με το μοντέλο χρώματος HSL με γενετική τέχνη και όχι RGB λόγω της ευκολίας χρήσης. Το RGB είναι λίγο αφηρημένο.

Αλληλεπίδραση χρήστη με το ποντίκι

Δεν έχει υπάρξει πραγματική αλληλεπίδραση χρήστη μέχρι αυτό το σημείο.

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

Αυτό το απλό αντικείμενο ενσωματώνει τη λογική των ενημερώσεων του ποντικιού από την υπόλοιπη σκηνή. Ενημερώνει μόνο το διάνυσμα θέσης σε μια κίνηση του ποντικιού. Τα υπόλοιπα αντικείμενα μπορούν στη συνέχεια να κάνουν δειγματοληψία από το διάνυσμα θέσης του ποντικιού αν έχουν περάσει μια αναφορά στο αντικείμενο. Μια προειδοποίηση που αγνοώ εδώ είναι αν το πλάτος του καμβά δεν είναι ένα προς ένα με τις διαστάσεις εικονοστοιχείων του DOM, δηλαδή έναν καμβά με αντοχή σε μεγέθυνση ή έναν καμβά υψηλότερης πυκνότητας εικονοστοιχείου (αμφιβληστροειδή) ή αν ο καμβάς δεν βρίσκεται στο πάνω αριστερά. Οι συντεταγμένες του ποντικιού θα πρέπει να προσαρμοστούν αναλόγως.

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

Το μόνο που έμεινε για το ποντίκι ήταν να δημιουργήσει το αντικείμενο του ποντικιού μέσα στη σκηνή. Τώρα που έχουμε ένα ποντίκι, ας προσελκύσουμε τις κουκίδες σε αυτό.

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

Έχω προσθέσει κάποιες κλιμακωτές τιμές στην κουκίδα έτσι ώστε ο καθένας να συμπεριφέρεται λίγο διαφορετικά στη προσομοίωση για να δώσει λίγο ρεαλισμό. Παίξτε γύρω με αυτές τις αξίες για να πάρετε μια διαφορετική αίσθηση. Τώρα στη μέθοδο προσέλκυσης ποντικιού. Είναι λίγο μακρύς με τα σχόλια.

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

Αυτή η μέθοδος θα μπορούσε να είναι λίγο συγκεχυμένη αν δεν είστε ενημερωμένοι στο διάνυσμα μαθηματικών σας. Οι διανύσματα μπορούν να είναι πολύ οπτικοί και μπορούν να σας βοηθήσουν αν τραβήξετε μερικά μουντζούρες έξω σε ένα χαρτί που χρωματίζεται με καφέ. Σε απλή αγγλική γλώσσα, αυτή η λειτουργία παίρνει την απόσταση μεταξύ του ποντικιού και της κουκκίδας. Στη συνέχεια, μετακινεί την κουκίδα λίγο πιο κοντά στην κουκίδα με βάση το πόσο κοντά είναι ήδη στην κουκίδα και το χρονικό διάστημα που έχει περάσει. Αυτό επιτυγχάνεται με την εξεύρεση της απόστασης που πρέπει να μετακινηθεί (κανονικός κλιμακωτός αριθμός) και, στη συνέχεια, πολλαπλασιάζοντας αυτήν με τον κανονικοποιημένο φορέα (ένα διάνυσμα με μήκος 1) της κουκκίδας που δείχνει προς το ποντίκι. Εντάξει, αυτή η τελευταία πρόταση δεν ήταν απαραιτήτως καθαρή αγγλική, αλλά είναι μια αρχή.