كنت دائمًا ... Canvas2D

هارون كراييسكي
هارون كراييسكي

في عالم الكثير من أدوات التظليل والشبكات والفلاتر، قد لا يثيرك تطبيق Canvas2D حماسك. ولكن يجب أن يتم ذلك! تحتوي 30% إلى 40% من صفحات الويب على عنصر <canvas>، في حين تستخدم 98% من جميع اللوحات سياق عرض لوحة الرسم "Canvas2D". هناك كاميرات Canvas2D في السيارات والثلاجات وفي الفضاء (في الواقع).

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

الجزء 1: الاطّلاع على آخر أخبار خدمة مقارنة الأسعار (CSS)

تتضمن CSS بعض أوامر الرسم المفقودة بشدة في Canvas2D. وباستخدام واجهة برمجة التطبيقات الجديدة، أضفنا عددًا من الميزات الأكثر طلبًا:

مستطيل ذو حواف دائرية

المستطيلات المستديرة: حجر الزاوية في الإنترنت، والحوسبة، وتقريبًا الحضارة.

بكل جدية، تعد المستطيلات المستديرة مفيدة للغاية: مثل الأزرار وفقاعات الدردشة والصور المصغرة والفقاعات التفسيرية الكلامية. لطالما كان من الممكن إنشاء مستطيل مستدير في Canvas2D، لقد كان بعض الفوضى فقط:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';

const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;

ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();

كان كل هذا ضروريًا لمستطيل مستدير بسيط وبسيط:

مستطيل مستدير الزوايا.

مع واجهة برمجة التطبيقات الجديدة، تتوفر طريقة roundRect().

ctx.roundRect(upper, left, width, height, borderRadius);

وبالتالي يمكن استبدال ما سبق بالكامل بما يلي:

ctx.roundRect(10, 10, 200, 100, 20);

تستخدم الطريقة ctx.roundRect() أيضًا صفيفًا للوسيطة borderRadius المكوّنة من أربعة أرقام كحدّ أقصى. تتحكم الأقطار هذه في الزوايا الأربعة للمستطيل الدائري بالطريقة نفسها التي تتحكم في CSS. مثلاً:

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

شاهِد هذا العرض التوضيحي.

تدرج مخروطي

لقد رأيت تدرجات لون خطية:

const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);

تدرج خطي.

التدرجات الشعاعية:

const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');

ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);

تدرج شعاعي.

ولكن ماذا عن التدرج المخروطي الرائع؟

const grad = ctx.createConicGradient(0, 100, 100);

grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');

ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);

تدرج مخروطي.

أدوات تعديل النص

لقد تخلفت إمكانات عرض نصوص Canvas2D بشكل كبير. أضاف Chrome العديد من السمات الجديدة إلى عرض نص Canvas2D:

تتطابق جميع هذه السمات مع نظيراتها في CSS التي تحمل الأسماء نفسها.

الجزء 2: تعديلات مريحة

في السابق، كانت بعض ميزات "Canvas2D" ممكنة، ولكن تنفيذها معقّد بلا داعٍ. في ما يلي بعض التحسينات على جودة الحياة لمطوّري JavaScript الذين يريدون استخدام Canvas2D:

إعادة ضبط السياق

لشرح كيفية محو لوحة ما، كتبتُ دالة صغيرة سخيفة لرسم نمط قديم:

draw90sPattern();

نمط من المثلثات والمربعات القديمة.

رائع! الآن بعد أن انتهيت من هذا النمط، أريد محو اللوحة ورسم شيء آخر. انتظر، كيف يتم محو لوحة مرة أخرى؟ أوه نعم! ctx.clearRect() بالطبع.

ctx.clearRect(0, 0, canvas.width, canvas.height);

عذرًا... لم يفلح ذلك. أوه نعم! يجب إعادة ضبط التحويل أولاً:

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
لوحة فارغة.

ممتاز. لوحة فارغة لطيفة. لنبدأ الآن في رسم خط أفقي رائع:

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

خط أفقي وخط قُطري.

يا له من أمر مزعج! إجابة غير صحيحة 😡 ماذا يفعل هذا السطر الإضافي هنا؟ لماذا هو زهري أيضًا؟ حسنًا، هيا نتحقق فقط من StackOverflow.

canvas.width = canvas.width;

لماذا هذا سخيف للغاية؟ لماذا هذه المهمة صعبة؟

حسنًا، لم يعد الأمر كذلك. وباستخدام واجهة برمجة التطبيقات الجديدة، نمتلك ابتكارات بسيطة وأنيقة ورائعة:

ctx.reset();

عذرًا، استغرق ذلك وقتًا طويلاً.

الفلاتر

تعتبر فلاتر SVG أمرًا في حد ذاته. وإذا كنت لا تزال مبتدئًا في مجال الفلاتر، أنصحك بشدة بقراءة مقالة The Art Of SVG Filters And Why It Is Awesome (فن فلاتر SVG ولماذا يعد رائعًا) الذي يعرض بعضًا من قدراته المذهلة.

تتوفر فلاتر نمط SVG حاليًا للوحة الرسم Canvas2D. وما عليك سوى الموافقة على تمرير عامل التصفية باعتباره عنوان URL يشير إلى عنصر فلتر SVG آخر على الصفحة:

<svg>
  <defs>
    <filter id="svgFilter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
      <feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
      <feColorMatrix type="hueRotate" values="90" />
    </filter>
  </defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);

مما يتلف نمطنا بشكل جيد جدًا:

يتم تطبيق النمط القديم مع تأثير مموَّه.

ولكن، ماذا لو أردت تنفيذ الإجراءات المذكورة أعلاه مع الحفاظ على استخدام JavaScript وعدم التلاعب بالسلاسل؟ وباستخدام واجهة برمجة التطبيقات الجديدة، أصبح ذلك ممكنًا تمامًا.

ctx.filter = new CanvasFilter([
  { filter: 'gaussianBlur', stdDeviation: 5 },
  {
    filter: 'convolveMatrix',
    kernelMatrix: [
      [-3, 0, 0],
      [0, 0.5, 0],
      [0, 0, 3],
    ],
  },
  { filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);

إنّها عملية سهلة مثل الفطيرة! جربه وتعامل مع المعلمات في العرض التوضيحي هنا.

الجزء الثالث: تحسينات على الأداء

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

ستتم القراءة بشكل متكرر.

يمكنك استخدام getImageData() لقراءة بيانات البكسل من لوحة. يمكن أن يكون بطيئًا جدًا. تمنحك واجهة برمجة التطبيقات الجديدة طريقة لوضع علامة واضحة على لوحة للقراءة (على سبيل المثال، للتأثيرات التوليدية). ويسمح لك هذا بتحسين العناصر المتقدمة والحفاظ على سرعة لوحة الرسم لمجموعة متنوعة من حالات الاستخدام. وقد توفرت هذه الميزة في Firefox منذ فترة، وسنجعلها أخيرًا جزءًا من مواصفات لوحة الرسم.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

فقدان السياق

لنجعل علامات التبويب الحزينة سعيدة مرة أخرى! في حالة نفاد ذاكرة وحدة معالجة الرسومات لدى العميل أو حدوث كارثة أخرى للوحة الرسم، يمكنك الآن تلقي معاودة الاتصال وإعادة الرسم حسب الحاجة:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

إذا كنت تريد قراءة المزيد عن سياق لوحة الرسم وما فقدانها، يمكنك الاطّلاع على شرح جيد في موقع WhatWG على ويكيته.

الخاتمة

سواء كنت مستخدمًا جديدًا لتطبيق Canvas2D، أو كنت تستخدمه منذ سنوات، أو كنت تتجنب استخدامها لسنوات، أنا هنا لأخبرك بإعطاء اللوحة مظهرًا آخر. إنها واجهة برمجة التطبيقات التي كانت موجودة طوال الوقت.

شكر وتقدير

صورة رئيسية من إعداد ساندي كلارك على Unsplash