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

Aaron Krajeski
Aaron Krajeski

في عالم تأثيرات التظليل والشبكات والفلاتر، قد لا يثيرك 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 مجموعة كبيرة من الخيارات. إذا كنت لا تعرف كيفية استخدامها، ننصحك بشدة بقراءة مقالة فن فلاتر 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 },
]);

خطوات سهلة وبسيطة جرِّب ذلك وغيِّر المَعلمات في العرض التجريبي هنا.

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

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

سيتمّ قراءتها بشكل متكرّر

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

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 أو كنت تستخدمه منذ سنوات أو كنت تتجنب استخدامه لعدة سنوات، ننصحك بإلقاء نظرة أخرى على Canvas. إنها واجهة برمجة التطبيقات المجاورة التي كانت متوفرة طوال الوقت.