التأثيرات الطباعية في اللوحة

خلفيتي

دخلت <canvas> وعيي في العام 2006 عند إطلاق الإصدار 2.0 من Firefox. مقالة عن أجاكسيان، تصف مصفوفة التحويل، ألهمتني بإنشاء أول تطبيق ويب لي <canvas>، وهو اللون الكروي (2007). لقد اندمجت أيضًا في عالم الألوان والرسومات الأساسية، وكان ذلك من خلال إنشاء تطبيق Sketchpad (2007-2008) في محاولة لإنشاء تطبيق "أفضل من Paint" في المتصفّح. وأدّت هذه التجارب في النهاية إلى إنشاء شركة Mugtug الناشئة مع صديقي القديم تشارلز بريشارد. نعمل على تطوير Darkroom في HTML5 <canvas>. Darkroom هو تطبيق لمشاركة الصور غير مدمر، يجمع بين قوى الفلاتر المستندة إلى وحدات البكسل وأسلوب الخط والرسم القائمين على المتجهات.

مقدمة

رسم بانر على لوحة الرسم

يتيح <canvas> لمبرمجي JavaScript التحكّم الكامل في الألوان والمتجهات والبكسل على شاشاتهم، أي التركيبة المرئية للشاشة.

تتناول الأمثلة التالية مجالاً واحدًا في <canvas> لم يجتذب الكثير من الاهتمام، ما يؤدي إلى إنشاء مؤثرات نصية. إنّ مجموعة المؤثرات النصية التي يمكن إنشاؤها في <canvas> أكبر ما يمكن أن تتخيله، فهذه الإصدارات التجريبية تتناول قسمًا فرعيًا من التأثيرات الممكنة. وبالرغم من أنّنا نتعامل مع "النص" في هذه المقالة، يمكن تطبيق الطرق على أي كائنات متجهات، ما يؤدي إلى إنشاء عناصر مرئية رائعة في الألعاب والتطبيقات الأخرى:

ظلال نصية في <canvas>.
تأثيرات نصية مماثلة لـ CSS في <canvas> تؤدي إلى إنشاء أقنعة اقتصاص، والعثور على مقاييس في <canvas>، واستخدام خاصية الظل
تأثيرات قوس قزح نيون وانعكاس الحمار الوحشي - سلاسل المحادثات
تأثيرات نصية شبيهة بالفوتوشوب في <canvas> مثال على استخدام globalCompositeOperation وcreateLinearGradent وcreatePattern.
ظلال داخلية وخارجية في <canvas>
الكشف عن ميزة بسيطة معروفة، وذلك من خلال استخدام اللفّات في اتجاه عقارب الساعة مقابل عكس اتجاه عقارب الساعة لإنشاء عكس الظل الخلفي (الظل الداخلي)
الفضاء التوليدي: التأثير التوليدي
تأثير نصي مستند إلى التوليدي في <canvas> باستخدام دورة الألوان hsl() وwindow.requestAnimationFrame لإضفاء طابع الحركة

ظلال النص في لوحة الرسم

إحدى الإضافات المفضلة لديّ إلى مواصفات CSS3 (بالإضافة إلى نصف قطر الحدود وتدرجات الويب وغير ذلك) هي القدرة على إنشاء ظلال. من المهم معرفة الاختلافات بين تظليل CSS و<canvas>، وتحديدًا ما يلي:

تستخدم CSS طريقتين هما: box-shadow لعناصر المربع، مثل div وspan وما إلى ذلك، وtext-shadow للمحتوى النصي.

يحتوي <canvas> على نوع واحد من الظلال، يُستخدم لجميع كائنات المتجه المتّجه، مثل ، ، ، ، ، ، ، ،،.، لإنشاء ظل في <canvas>، انقر على السمات الأربعة التالية:

ctx.shadowColor = "red" // string
لون الظل صحيح، أي ألوان RGB وRGBA وHSL وHEX وغيرها من الإدخالات صالحة.
ctx.shadowOffsetX = 0; // عدد صحيح
المسافة الأفقية للظل بالنسبة إلى النص
ctx.shadowOffsetY = 0; // integer
المسافة العمودية للظل بالنسبة إلى النص
ctx.shadowBlur = 10; // عدد صحيح
كلّما زاد تأثير التمويه في الظل، زادت القيمة.

للبدء، سنتعرّف على طريقة محاكاة <canvas> لتأثيرات CSS. لقد أدى البحث في صور Google عن "ظل نص css" إلى بعض العروض التوضيحية الرائعة التي أصبح بإمكاننا محاكاتها، مثل Line25 وStereoscopic وShadow 3D.

رسم CSS ثلاثي الأبعاد

يعد التأثير الثلاثي الأبعاد المجسَّم (راجع صورة الصورة التمثيلية لمزيد من المعلومات) مثالاً على سطر بسيط من الرمز، ويمكن استخدامه على نحو رائع. باستخدام السطر التالي في CSS، يمكننا خلق وهم بالعمق عند مشاهدته باستخدام نظارات ثلاثية الأبعاد حمراء/سماوية (النوع الذي تقدمه لك في الأفلام ثلاثية الأبعاد):

text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;

هناك أمران يجب ملاحظتهما عند تحويل هذه السلسلة إلى <canvas>:

  1. لا يوجد تمويه للظل (القيمة الثالثة)، لذلك لا يوجد سبب لتشغيل الظل في الواقع، حيث سينشئ fillText النتائج نفسها:
var text = "Hello world!"
ctx.fillStyle = "#000"
ctx.fillText(text, -7, 0);
ctx.fillStyle = "red"
ctx.fillText(text, 0, 0);
ctx.fillStyle = "cyan"
ctx.fillText(text, 7, 0);</pre>
  1. قيم EM غير متوافقة مع <canvas>، لذا يجب تحويلها إلى PX. يمكننا العثور على نسبة التحويل بين PT وPC وEM وEX وPX وما إلى ذلك من خلال إنشاء عنصر بنفس خصائص الخط في DOM، وتعيين العرض على التنسيق المراد قياسه؛ أو على سبيل المثال، لتسجيل تحويل EM -> PX، فإننا نقيس عنصر DOM بـ "height: 1em"، وتساوي قيمة
var font = "20px sans-serif"
var d = document.createElement("span");
d.style.cssText = "font: " + font + " height: 1em; display: block"
// the value to multiply PX 's by to convert to EM 's
var EM2PX = 1 / d.offsetHeight;</pre>

منع ضرب ألفا

في مثال أكثر تعقيدًا، مثل تأثير النيون الموجود في Line25، يجب استخدام خاصية shadowBlur لمحاكاة التأثير بشكل صحيح. بما أنّ تأثير النيون يعتمد على ظلال متعدّدة، نواجه مشكلة، في <canvas> يمكن أن يحتوي كل كائن متّجه على ظل واحد فقط. لذلك، من أجل رسم ظلال متعددة، يجب رسم نسخ متعددة من النص فوق نفسه. ينتج عن ذلك ضرب ألفا، وفي نهاية المطاف حواف مسنّنة.

رسم نيون

لقد حاولت تشغيل ctx.fillStyle = "rgba(0,0,0,0)" أو "transparent" لإخفاء النص أثناء عرض الظل... ومع ذلك، لم تكن هذه المحاولة مجدية؛ نظرًا لأن الظل يمثل ضربًا في ألفا fillStyle، لا يمكن أن يكون الظل معتمًا أكثر من fillStyle.

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

var text = "Hello world!"
var blur = 10;
var width = ctx.measureText(text).width + blur * 2;
ctx.textBaseline = "top"
ctx.shadowColor = "#000"
ctx.shadowOffsetX = width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width, 0);

اقتصاص كتلة نص

لتنظيف هذا قليلاً، يمكننا منع رسم fillText في المقام الأول (مع السماح برسم الظل) عن طريق إضافة مسار قص. لإنشاء مسار اقتصاص يحيط بالنص، نحتاج إلى معرفة ارتفاع النص (يُسمى "em-height" سابقًا ارتفاع الحرف "M" على مطبعة الطباعة) وعرض النص. يمكننا الحصول على العرض باستخدام ctx.measureText().width، ومع ذلك، ctx.measureText().height غير موجود.

لحسن الحظ، من خلال عملية اختراق CSS (الاطّلاع على المقاييس الطباعية للاطّلاع على مزيد من الطرق لإصلاح عمليات التنفيذ القديمة لـ <canvas> باستخدام قياسات CSS)، يمكننا العثور على ارتفاع النص من خلال قياس offsetHeight في <span> باستخدام خصائص الخط نفسها:

var d = document.createElement("span");
d.font = "20px arial"
d.textContent = "Hello world!"
var emHeight = d.offsetHeight;

من هناك، يمكننا إنشاء مستطيل لاستخدامه كمسار اقتصاص؛ حيث يضم "الظل" أثناء إزالة الشكل الوهمي.

ctx.rect(0, 0, width, emHeight);
ctx.clip();

من خلال تجميع كل العناصر معًا، وتحسينها مع مرور الوقت، إذا لم يكن للظل أي تمويه، يمكن استخدام fillText للتأثير نفسه، ما يوفر لنا من إعداد قناع الاقتصاص:

var width = ctx.measureText(text).width;
var style = shadowStyles[text];
// add a background to the current effect
ctx.fillStyle = style.background;
ctx.fillRect(0, offsetY, ctx.canvas.width, textHeight - 1)
// parse text-shadows from css
var shadows = parseShadow(style.shadow);
// loop through the shadow collection
var n = shadows.length; while(n--) {
var shadow = shadows[n];
var totalWidth = width + shadow.blur * 2;
ctx.save();
ctx.beginPath();
ctx.rect(offsetX - shadow.blur, offsetY, offsetX + totalWidth, textHeight);
ctx.clip();
if (shadow.blur) { // just run shadow (clip text)
    ctx.shadowColor = shadow.color;
    ctx.shadowOffsetX = shadow.x + totalWidth;
    ctx.shadowOffsetY = shadow.y;
    ctx.shadowBlur = shadow.blur;
    ctx.fillText(text, -totalWidth + offsetX, offsetY + metrics.top);
} else { // just run pseudo-shadow
    ctx.fillStyle = shadow.color;
    ctx.fillText(text, offsetX + (shadow.x||0), offsetY - (shadow.y||0) + metrics.top);
}
ctx.restore();
}
// drawing the text in the foreground
if (style.color) {
ctx.fillStyle = style.color;
ctx.fillText(text, offsetX, offsetY + metrics.top);
}
// jump to next em-line
ctx.translate(0, textHeight);

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

ظل النص في تأثيرات اللوحة

الفاصل (الظل عند الدفع بوحدات البكسل)

في كتابة هذا القسم من المقالة، جعلني المثال المجسم فضوليًا. ما مدى صعوبة إنشاء تأثير لشاشة فيلم ثلاثي الأبعاد باستخدام <canvas> وصورتين تم التقاطهما من منظورين مختلفين قليلاً؟ على ما يبدو، ليس صعبًا جدًا. تجمع النواة التالية بين القناة الحمراء للصورة الأولى (البيانات) مع القناة الزرقاء للصورة الثانية (data2):

data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;

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

نظارات ثلاثية الأبعاد

تأثير قوس قزح نيون وانعكاس الحمار الوحشي

إنّ تسلسل تأثيرات متعدّدة في "<canvas>" قد يكون أمرًا بسيطًا، ولكن يتطلّب الأمر معرفة أساسية بـ GlobalCompositeOperation (GCO). لمقارنة العمليات مع GIMP (أو Photoshop): هناك 12 من GCO باللون <canvas> أغمق، ويمكن اعتبار أخف كأوضاع دمج الطبقات، حيث يتم تطبيق العمليات العشر الأخرى على الطبقات باعتبارها أقنعة ألفا (تزيل طبقة واحدة وحدات البكسل في الطبقة الأخرى). يربط GlobalCompositeOperation بـ "الطبقات" (أو في حالتنا، سلاسل التعليمات البرمجية) معًا، ودمجها بطرق جديدة ومثيرة:

رسومات سلسلة المؤثرات

يُظهر مخطط GlobalCompositeOperation أوضاع عمل GCO أثناء عمله، ويستخدم هذا الرسم البياني جزءًا كبيرًا من طيف الألوان ومستويات متعددة من شفافية ألفا لمعرفة ما يمكن توقعه بالتفصيل. أنصحك بالتحقق من مرجع GlobalCompositeOperation من Mozilla للأوصاف النصية. لمزيد من الأبحاث، يمكنك معرفة كيفية عمل العملية في مقالة Compositing Digital Images لمؤلفة بورتر داف.

الوضع المفضل لدي هو globalCompositeOperation="lighter". يمزج الضوء الأخف من وحدات البكسل الملحقة على غرار مزج الضوء؛ عندما يكون الضوء الأحمر والأخضر والأبيض بكثافة كاملة، يظهر الضوء الأبيض. إنّها ميزة مشوّقة، خاصةً عند ضبط <canvas> على إصدار ألفا عالمي منخفض، ما يتيح إمكانية التحكّم بشكل أدق وحواف أكثر سلاسة. وقد تم استخدام Lighter العديد من الاستخدامات، وأهم ما أحب أن يكون منشئ محتوى لخلفية سطح المكتب بتنسيق HTML5 على الرابط http://weavesilk.com/. أحد عروضي التوضيحية، Breathing Galaxy (JS1k) يستخدم أيضًا الوضع الأخف - عند رسم أنماط من هذين المثالين، تبدأ في معرفة التأثير الذي ينتجه هذا الوضع.

معالجة المتصفّح للمكوّن الإضافيglobalCompositeOperation.

تأثير عدم استقرار قوس قزح نيون

في العرض التوضيحي التالي، سنحقق لمعان نيون قوس قزح يشبه برنامج Photoshop في تصميم مخطط متقطع، وذلك من خلال تسلسل التأثيرات معًا باستخدام العملية العالمية المركّبة (source-in، وAإفتح، وأكثر قتامة). هذا العرض التوضيحي هو تقدمة في العرض التوضيحي "Text-Shadows in <canvas>"، باستخدام نفس الاستراتيجية لفصل الظل عن النص (راجع القسم السابق):

عدم الاستقرار بألوان قوس قزح
function neonLightEffect() {
var text = "alert('"+String.fromCharCode(0x2665)+"')";
var font = "120px Futura, Helvetica, sans-serif";
var jitter = 25; // the distance of the maximum jitter
var offsetX = 30;
var offsetY = 70;
var blur = getBlurValue(100);
// save state
ctx.save();
ctx.font = font;
// calculate width + height of text-block
var metrics = getMetrics(text, font);
// create clipping mask around text-effect
ctx.rect(offsetX - blur/2, offsetY - blur/2,
        offsetX + metrics.width + blur, metrics.height + blur);
ctx.clip();
// create shadow-blur to mask rainbow onto (since shadowColor doesn't accept gradients)
ctx.save();
ctx.fillStyle = "#fff";
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowOffsetX = metrics.width + blur;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -metrics.width + offsetX - blur, offsetY + metrics.top);
ctx.restore();
// create the rainbow linear-gradient
var gradient = ctx.createLinearGradient(0, 0, metrics.width, 0);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
// change composite so source is applied within the shadow-blur
ctx.globalCompositeOperation = "source-atop";
// apply gradient to shadow-blur
ctx.fillStyle = gradient;
ctx.fillRect(offsetX - jitter/2, offsetY,
            metrics.width + offsetX, metrics.height + offsetY);
// change composite to mix as light
ctx.globalCompositeOperation = "lighter";
// multiply the layer
ctx.globalAlpha = 0.7
ctx.drawImage(ctx.canvas, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalAlpha = 1
// draw white-text ontop of glow
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(text, offsetX, offsetY + metrics.top);
// created jittered stroke
ctx.lineWidth = 0.80;
ctx.strokeStyle = "rgba(255,255,255,0.25)";
var i = 10; while(i--) { 
    var left = jitter / 2 - Math.random() * jitter;
    var top = jitter / 2 - Math.random() * jitter;
    ctx.strokeText(text, left + offsetX, top + offsetY + metrics.top);
}    
ctx.strokeStyle = "rgba(0,0,0,0.20)";
ctx.strokeText(text, offsetX, offsetY + metrics.top);
ctx.restore();
};

تأثير انعكاس حمار وحشي

تم استلهام تأثير Zebra Reflection من خلال مورد WebDesignerWall الممتاز حول كيفية تحسين محتوى صفحتك باستخدام CSS. يأخذ ذلك الفكرة إلى أبعد من ذلك قليلاً، وينشئ "انعكاسًا" للنص، مثل ما قد تراه في iTunes. يجمع التأثير بين fillColor (أبيض) وcreatePattern (zebra.png) و lineGradent (shine)؛ وهذا يوضح القدرة على تطبيق أنواع تعبئة متعددة على كل كائن متجه:

تأثير الحمار الوحشي
function sleekZebraEffect() {
// inspired by - http://www.webdesignerwall.com/demo/css-gradient-text/
var text = "Sleek Zebra...";
var font = "100px Futura, Helvetica, sans-serif";

// save state
ctx.save();
ctx.font = font;

// getMetrics calculates:
// width + height of text-block
// top + middle + bottom baseline
var metrics = getMetrics(text, font);
var offsetRefectionY = -20;
var offsetY = 70;
var offsetX = 60;

// throwing a linear-gradient in to shine up the text
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.1, '#000');
gradient.addColorStop(0.35, '#fff');
gradient.addColorStop(0.65, '#fff');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient
ctx.fillText(text, offsetX, offsetY + metrics.top);

// draw reflected text
ctx.save();
ctx.globalCompositeOperation = "source-over";
ctx.translate(0, metrics.height + offsetRefectionY)
ctx.scale(1, -1);
ctx.font = font;
ctx.fillStyle = "#fff";
ctx.fillText(text, offsetX, -metrics.height - offsetY + metrics.top);
ctx.scale(1, -1);

// cut the gradient out of the reflected text 
ctx.globalCompositeOperation = "destination-out";
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.0, 'rgba(0,0,0,0.65)');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient;
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height);

// restore back to original transform state
ctx.restore();

// using source-atop to allow the transparent .png to show through to the gradient
ctx.globalCompositeOperation = "source-atop";

// creating pattern from <image> sourced.
ctx.fillStyle = ctx.createPattern(image, 'repeat');

// fill the height of two em-boxes, to encompass both normal and reflected state
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height * 2);
ctx.restore();
};

الظلال الداخلية/الخارجية في لوحة الرسم

لا تتطرق مواصفات <canvas> إلى موضوع الظلال "الداخلية" مقارنةً بالظلال "الخارجية". في الواقع، قد تتوقع للوهلة الأولى عدم دعم الظل "الداخلي". ولكن الأمر ليس كذلك. إنّ تفعيل هذه الميزة أسهل قليلاً ;) وكما هو مقترح في مشاركة حديثة من F1LT3R، يمكنك إنشاء ظلال داخلية باستخدام الخصائص الفريدة لقواعد اتجاه عقارب الساعة مقابل عكس اتجاه عقارب الساعة. للقيام بذلك، يمكنك إنشاء "ظل داخلي" عن طريق رسم مستطيل حاوية، ثم باستخدام قواعد متعرجة معاكسة، رسم شكل مقطوع، ما يؤدي إلى إنشاء معكوس الشكل.

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

ظلال داخلية وخارجية
function innerShadow() {

function drawShape() { // draw anti-clockwise
ctx.arc(0, 0, 100, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(70, 0);
ctx.arc(0, 0, 70, 0, Math.PI, false); // Mouth
ctx.moveTo(-20, -20);
ctx.arc(30, -30, 10, 0, Math.PI * 2, false); // Left eye
ctx.moveTo(140, 70);
ctx.arc(-20, -30, 10, 0, Math.PI * 2, false); // Right eye
};

var width = 200;
var offset = width + 50;
var innerColor = "rgba(0,0,0,1)";
var outerColor = "rgba(0,0,0,1)";

ctx.translate(150, 170);

// apply inner-shadow
ctx.save();
ctx.fillStyle = "#000";
ctx.shadowColor = innerColor;
ctx.shadowBlur = getBlurValue(120);
ctx.shadowOffsetX = -15;
ctx.shadowOffsetY = 15;

// create clipping path (around blur + shape, preventing outer-rect blurring)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
ctx.clip();

// apply inner-shadow (w/ clockwise vs. anti-clockwise cutout)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
drawShape();
ctx.fill();
ctx.restore();

// cutout temporary rectangle used to create inner-shadow
ctx.globalCompositeOperation = "destination-out";
ctx.fill();

// prepare vector paths
ctx.beginPath();
drawShape();

// apply fill-gradient to inner-shadow
ctx.save();
ctx.globalCompositeOperation = "source-in";
var gradient = ctx.createLinearGradient(-offset/2, 0, offset/2, 0);
gradient.addColorStop(0.3, '#ff0');
gradient.addColorStop(0.7, '#f00');
ctx.fillStyle = gradient;
ctx.fill();

// apply fill-pattern to inner-shadow
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 1;
ctx.rotate(0.9);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();

// apply fill-gradient
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var gradient = ctx.createLinearGradient(-offset/2, -offset/2, offset/2, offset/2);
gradient.addColorStop(0.1, '#f00');
gradient.addColorStop(0.5, 'rgba(255,255,0,1)');
gradient.addColorStop(1.0, '#00f');
ctx.fillStyle = gradient
ctx.fill();

// apply fill-pattern
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 0.2;
ctx.rotate(-0.4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();

// apply outer-shadow (color-only without temporary layer)
ctx.globalCompositeOperation = "destination-over";
ctx.shadowColor = outerColor;
ctx.shadowBlur = 40;
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 10;
ctx.fillStyle = "#fff";
ctx.fill();
};

من هذه الأمثلة، يمكنك أن ترى، باستخدام GlobalCompositeOperation، يمكننا ربط التأثيرات معًا وإنتاج تأثيرات أكثر تفصيلاً (باستخدام الإخفاء والمزج). ستكون الشاشة هي المحاربة لديك ؛)

الفضاء الخارجي — التأثيرات التوليدية

في <canvas>، الانتقال من حرف يونيكود 0x2708:

صورة يونيكود

...إلى هذا المثال المظلل:

مثال مظلل

...يمكن تحقيقها من خلال استدعاءات متعددة لـ ctx.strokeText() باستخدام خط رفيع (0.25) مع تقليل إزاحة x وألفا ببطء، ما يمنح عناصر المتجه لدينا الشعور بالحركة.

من خلال تعيين الموضع سص على موجة جيبية/جيب التمام، والتنقل بين الألوان باستخدام خاصية HSL، يمكننا إنشاء تأثيرات أكثر إثارة للاهتمام، مثل مثال "الخطر البيولوجي" هذا:

تأثير ركوب الدراجات HSL

HSL: تدرج اللون، تشبع اللون، الإضاءة (1978)

تنسيق HSL هو تنسيق متوافق حديثًا في مواصفات CSS3. حيث تم تصميم HEX لأجهزة الكمبيوتر، تم تصميم HSL حتى يكون قابلاً للقراءة البشرية.

لتوضيح سهولة استخدام HSL؛ للتنقل عبر طيف الألوان، يمكننا ببساطة زيادة "تدرج اللون" من 360؛ يتم تعيين تدرج اللون إلى الطيف على شكل أسطواني. تتحكم الإضاءة في مدى قتامة/إضاءة اللون؛ تشير 0% إلى بكسل أسود، بينما تشير نسبة 100% إلى بكسل أبيض. يتحكم التشبع في مدى سطوع اللون أو سطوعه؛ يتم إنشاء اللون الرمادي بتشبع بنسبة 0٪، ويتم إنشاء الألوان الزاهية باستخدام قيمة 100٪.

رسم HSL

ولأنّ تقنية HSL هي معيار حديث، ننصحك بمواصلة التوافق مع المتصفّحات القديمة، وهو ما يمكن ذلك من خلال تحويل اللون إلى مساحات مختلفة. يقبل الرمز التالي كائن HSL { H: 360, S: 100, L: 100} ويُخرج كائن نموذج أحمر أخضر أزرق { R: 255, G: 255, B: 255 }. من هناك، يمكنك استخدام هذه القيم لإنشاء سلسلة نموذج أحمر أخضر أزرق أو نموذج أحمر أخضر أزرق (RGB). لمزيد من المعلومات المتعمقة، راجع مقالة Wikipedia المفيدة حول HSL.

// HSL (1978) = H: Hue / S: Saturation / L: Lightness
HSL_RGB = function (o) { // { H: 0-360, S: 0-100, L: 0-100 }
var H = o.H / 360,
    S = o.S / 100,
    L = o.L / 100,
    R, G, B, _1, _2;

function Hue_2_RGB(v1, v2, vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
return v1;
}

if (S == 0) { // HSL from 0 to 1
R = L * 255;
G = L * 255;
B = L * 255;
} else {
if (L < 0.5) {
    _2 = L * (1 + S);
} else {
    _2 = (L + S) - (S * L);
}
_1 = 2 * L - _2;

R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
G = 255 * Hue_2_RGB(_1, _2, H);
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
}

return {
R: R,
G: G,
B: B
};
};

إنشاء صور متحركة باستخدام requestAnimationFrame

في السابق، كان هناك خياران لإنشاء الصور المتحركة باستخدام JavaScript، وهما setTimeout وsetInterval.

window.requestAnimationFrame، هو المعيار الجديد الذي حلّ محلّ كليهما، أي توفير استهلاك الطاقة الكهربائية (ومن جهاز الكمبيوتر بضع ضربات قلبية) من خلال السماح للمتصفح بتنظيم الصور المتحركة استنادًا إلى الموارد المتاحة. وتشمل بعض الميزات المهمة ما يلي:

  • عندما يتوفر المستخدم للإطار، يمكن أن تبطئ الحركة أو تتوقف تمامًا، لمنع استخدام الموارد غير اللازمة.
  • هناك حد أقصى لعدد اللقطات في الثانية وهو 60 لقطة في الثانية. السبب في ذلك هو أنه أعلى بكثير من المستوى الذي يمكن أن يلاحظه البشر (يرى معظم البشر بنسبة 30 لقطة في الثانية أن الرسوم المتحركة "سلسة").

في وقت كتابة هذا التقرير، يجب عرض بادئات محدّدة لاستخدام requestAnimationFrame. أنشأ بول أيرلند طبقة رقيقة تدعم بائعين متعددين، وذلك في requestAnimationFrame للصور المتحركة الذكية:

// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return  window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     || 
        function(/* function */ callback, /* DOMElement */ element){
        window.setTimeout(callback, 1000 / 60);
        };
})();

وبالتالي، بإمكان أي شخص طموح أكثر أن يربط هذا الترميز مع رمز poly-fill التالي، مثل requestAnimationFrame.js (هناك بعض الميزات المطلوب تطويرها) والتي ستتوافق مع المتصفّحات القديمة إلى حدّ أبعد، مع التبديل إلى هذا المعيار الجديد.

(function animate() {
var i = 50;
while(i--) {
    if (n > endpos) return;

    n += definition;
    ctx.globalAlpha = (0.5 - (n + startpos) / endpos) * alpha;
    if (doColorCycle) {
        hue = n + color;
        ctx.strokeStyle = "hsl(" + (hue % 360) + ",99%,50%)"; // iterate hue
    }
    var x = cos(n / cosdiv) * n * cosmult; // cosine
    var y = sin(n / sindiv) * n * sinmult; // sin
    ctx.strokeText(text, x + xoffset, y + yoffset); // draw rainbow text
}
timeout = window.requestAnimationFrame(animate, 0);
})();
رسم تمويه للملاحظات
رسم متحرك
رسم المصفوفة

رمز المصدر

بفضل الدعم من جميع بائعي المتصفح، ما مِن سؤال حول مستقبل <canvas> الذي يمكن نقله إلى الملفات التنفيذية على iPhone/Android/Desktop باستخدام PhoneGap، أو

التيتانيوم: