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