Canvas2D همیشه تو بودی

آرون کراجسکی
Aaron Krajeski

در دنیای سایه‌زن‌ها، مش‌ها و فیلترها، Canvas2D ممکن است شما را هیجان‌زده نکند. اما باید! 30 تا 40 درصد از صفحات وب دارای عنصر <canvas> هستند و 98 درصد از تمام بوم ها از یک زمینه رندر Canvas2D استفاده می کنند. Canvas2D در اتومبیل‌ها، یخچال‌ها و در فضا (واقعاً) وجود دارد.

مسلماً، API کمی عقب‌تر از زمان طراحی دوبعدی پیشرفته است. خوشبختانه ما سخت در حال پیاده سازی ویژگی های جدید در Canvas2D برای رسیدن به CSS، ساده سازی ارگونومی و بهبود عملکرد بوده ایم.

بخش 1: رسیدن به CSS

CSS دارای چند دستور طراحی است که به شدت در Canvas2D وجود ندارد. با API جدید تعدادی از ویژگی های درخواستی را اضافه کرده ایم:

راست گرد

مستطیل های گرد: سنگ بنای اینترنت، محاسبات، نزدیک، تمدن.

با جدیت تمام، مستطیل های گرد بسیار مفید هستند: به عنوان دکمه ها، حباب های چت، تصاویر کوچک، حباب های گفتار، شما آن را نام ببرید. همیشه امکان ایجاد یک مستطیل گرد در 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();

همه اینها برای یک مستطیل گرد ساده و ساده ضروری بود:

یک مستطیل گرد.

با API جدید یک متد 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);

یک گرادیان مخروطی.

اصلاح کننده های متن

قابلیت های رندر متن Canvas2Ds به طرز غم انگیزی عقب مانده است. Chrome چندین ویژگی جدید به رندر متن Canvas2D اضافه کرده است:

همه این ویژگی ها با همتایان CSS خود با نام های یکسان مطابقت دارند.

بخش 2: ترفندهای ارگونومیک

پیش از این، برخی از چیزها با Canvas2D امکان پذیر بود، اما پیاده سازی بیهوده پیچیده بود. در اینجا برخی از بهبودهای کیفیت زندگی برای توسعه دهندگان جاوا اسکریپت که می خواهند از 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;

این چرا اینقدر احمقانه است؟ چرا اینقدر سخته؟

خب دیگه نیست با API جدید، پیشگامی ساده، ظریف و زیبا را داریم:

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

که الگوی ما را به خوبی به هم می زند:

الگوی یکپارچهسازی با سیستمعامل با جلوه تار اعمال شده است.

اما، اگر بخواهید کارهای بالا را انجام دهید اما در جاوا اسکریپت بمانید و رشته ها را به هم نزنید، چه؟ با API جدید، این کاملا امکان پذیر است.

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: بهبود عملکرد

با New Canvas2D API، ما همچنین می‌خواستیم عملکرد را در صورت امکان بهبود دهیم. ما چند ویژگی اضافه کردیم تا به توسعه دهندگان کنترل دقیق تری بر وب سایت هایشان بدهیم و به نرم ترین نرخ فریم های ممکن اجازه دهیم:

مکرر خواهد خواند

از getImageData() برای بازخوانی داده های پیکسل از روی بوم استفاده کنید. می تواند بسیار کند باشد. API جدید راهی برای علامت‌گذاری صریح یک بوم برای خواندن مجدد (مثلاً برای جلوه‌های تولیدی) به شما می‌دهد. این به شما امکان می‌دهد چیزهای زیر کاپوت را بهینه کنید و بوم را برای موارد استفاده متنوع‌تر سریع نگه دارید. این ویژگی برای مدتی در فایرفاکس وجود دارد و ما در نهایت آن را بخشی از مشخصات بوم می کنیم.

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

از دست دادن زمینه

دوباره تب های غمگین را شاد کنیم! در صورتی که حافظه GPU یک کلاینت تمام شود یا فاجعه دیگری روی بوم شما بیفتد، اکنون می توانید یک تماس پاسخ دریافت کنید و در صورت نیاز دوباره ترسیم کنید:

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

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

اگر می‌خواهید در مورد بافت بوم و از دست دادن بیشتر بخوانید، WHATWG توضیح خوبی در ویکی خود دارد.

نتیجه گیری

فرقی نمی‌کند با Canvas2D تازه کار هستید، سال‌ها است که از آن استفاده می‌کنید، یا سال‌ها از استفاده از آن اجتناب می‌کنید، من اینجا هستم تا به شما بگویم ظاهر دیگری به بوم ببخشید. این API-next-door است که همیشه وجود داشته است.