رمز جميل هو متعة للكتابة ، ولكن من الصعب مشاركة هذا الفرح مع المبرمجين الآخرين ، ناهيك عن غير المبرمجين. في وقت الفراغ بين وظيفتي اليومية ووقت العائلة ، كنت أتلاعب بفكرة قصيدة البرمجة باستخدام عنصر لوحة الرسم في المتصفح. هناك العديد من المصطلحات لوصف التجارب البصرية على الكمبيوتر مثل الفن ديف ورسم الكود التجريبي والفن التفاعلي ، ولكن في النهاية قمت بتسوية قصيدة البرمجة لوصف هذه العملية. إن الفكرة وراء قصيدة هي قطعة نثر مصقولة يمكن مشاركتها بسهولة وموجزة وجمالية. انها ليست فكرة نصف منتهية في كراسة الرسم ، ولكن قطعة متماسكة قدم للمشاهد من أجل التمتع بها. القصيدة ليست أداة ، ولكنها موجودة لإثارة العاطفة.

من أجل الاستمتاع الخاص بي ، كنت أقرأ الكتب في الرياضيات والحساب والفيزياء والبيولوجيا. لقد تعلمت بسرعة أنه عندما أضع فكرة ، فإنه يدمر الناس بسرعة كبيرة. بصريا يمكنني أن أتناول بعضًا من هذه الأفكار التي أجدها رائعة ، وسرعان ما أعطي أي شخص شعوراً بالدهشة ، حتى لو لم يفهموا النظرية الكامنة وراء الكود والمفاهيم التي تحركه. لا تحتاج إلى معالجة أي فلسفة أو رياضيات صلبة لكتابة قصيدة برمجة ، مجرد رغبة في رؤية شيء ما على الهواء مباشرة والتنفس على الشاشة.

إن الكود والأمثلة التي وضعتها معًا أدناه ستجعلنا نفهم كيفية سحب هذه العملية السريعة والمرضية للغاية. إذا كنت ترغب في المتابعة مع الرمز يمكنك ذلك قم بتنزيل الملفات المصدر هنا.

الخدعة الرئيسية عند إنشاء قصيدة هي إبقائها خفيفة وبسيطة. لا تقضي ثلاثة أشهر في بناء عرض واحد رائع حقًا. بدلا من ذلك ، قم بإنشاء 10 قصائد تطوّر فكرة. اكتب رمزًا تجريبيًا مثيرًا ، ولا تخف من الفشل.

مقدمة إلى قماش

وللحصول على نظرة عامة سريعة ، فإن اللوحة هي في جوهرها عنصر صورة نقطية ثنائية الأبعاد تعيش في DOM والتي يمكن رسمها. يمكن أن يتم الرسم باستخدام سياق ثنائي الأبعاد أو سياق WebGL. السياق هو كائن JavaScript الذي تستخدمه للوصول إلى أدوات الرسم. إن أحداث JavaScript المتوفرة لقماش الكتّاب مجردة جدًا ، بخلاف تلك المتاحة لـ 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 و font و strokeStyle قبل استخدام استدعاء السحب الفعلي. من السهل نسيان تحديث هذه الإعدادات أو إعادة تعيينها والحصول على بعض النتائج غير المقصودة.

مما يجعل الأشياء تتحرك

المثال الأول فقط ركض مرة واحدة ورسم صورة ثابتة على اللوحة. هذا أمر جيد ، ولكن عندما يستمتع حقا هو عندما يتم تحديثه في 60 لقطة في الثانية. تحتوي المتصفحات الحديثة على الطلب المدمج في وظيفة AnimationFrame الذي يقوم بمزامنة رمز الرسم المخصص لدورات الرسم الخاصة بالمتصفح. هذا يساعد من حيث الكفاءة والسلاسة. يجب أن يكون هدف التصور هو الشفرة التي يبلغ طولها 60 إطارًا في الثانية.

(ملاحظة حول الدعم: توجد بعض polyfills بسيطة إذا كنت بحاجة إلى دعم المتصفحات القديمة.)

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

الآن في المشهد ، بدلاً من إنشاء وتحديث نقطة ، نقوم بإنشاء وتحديث DotManager . سننشئ 5000 نقطة للبدء.

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

لكل نقطة جديدة تم إنشاؤها ، خذ موقعها الأولي ، وحدد لونه إلى حيث يكون على طول عرض اللوحة. الدالة Utils.hslToFillStyle هي دالة مساعد صغيرة أضفتها لتحويل بعض متغيرات الإدخال إلى سلسلة 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) للنقطة التي تشير إلى الماوس. حسناً ، هذه الجملة الأخيرة لم تكن بالضرورة واضحة للإنجليزية ، لكنها بداية.