اگر از آموزشهای پیش به یاد داشته باشید، گفتیم ویژگیهایی برای تغییر تعامل شکل فعلی و لایهی ترسیمات وجود دارند که میتوانند به کلی ترسیمات نهایی درون canvas را تغییر دهند. در این بخش به این ویژگیها میپردازیم و برخی از کاربردهای مهم آنها را بررسی میکنیم. پیش از آغاز، توجه کنید که شکل فعلی فقط زمانی با لایهی ترسیمات تعامل خواهد داشت که بخواهد رسم شود. در غیر این صورت این ویژگیها هیچ تاثیری نخواهند داشت.
ویژگی globalAlpha
این ویژگی یک شفافیت کلی برای تمام ترسیمات تعیین میکند. مقدار پیشفرض آن 1 (بدون شفافیت) است و همیشه عددی بین 0 و 1 میپذیرد. اگر عددی خارج از این بازه به آن بدهیم، مقدار آن به 1 برمیگردد. ممکن است با خود بگویید «با وجود فرمتهای رنگ مانند rgba
که درون خود شفافیت دارند، چه نیازی به این ویژگی است؟» پاسخ این است که مواردی مانند تصویر وجود دارند که نمیتوان برای آنها به کمک rgba
شفافیت ایجاد کرد. در این موارد این ویژگی کاربرد دارد. به نمونه کد زیر دقت کنید:
ctx.fillStyle = "#111";
ctx.fillRect(0, 0, cvs.width, cvs.height);
ctx.globalAlpha = 0.5;
ctx.arc(cvs.width / 2, cvs.height / 2, 300, 0, Math.PI * 2);
ctx.fillStyle = "#3D3";
ctx.fill();
ctx.fillStyle = "#0AE";
ctx.fillRect(50, 50, 200, 300);
در کد بالا تمام ترسیمات دارای 50% شفافیت خواهند بود. پسزمینهی سیاه از این جهت اضافه شده که بتوانید بهتر شفافیت را تشخیص دهید. جدا از بحث ایجاد شفافیت برای تصاویر، این ویژگی کاربرد فوقالعادهی دیگری نیز دارد که برای آشنایی با آن ابتدا باید به ویژگی بعدی بپردازیم.
ویژگی globalCompositeOperation
این ویژگی شبیه به نام نسبتا طولانی که دارد، مقدارهای زیادی نیز میپذیرد! این ویژگی نوع تعامل ترسیمات جدیدی که قرار است به لایهی ترسیمات اضافه شوند را با ترسیمات قبلی مشخص میکند. این ویژگی مقادیر زیر را میپذیرد که به توضیح هر یک میپردازیم:
ctx.globalCompositeOperation = "source-over" || "source-in" || "source-out" ||
"source-atop" || "destination-over" || "destination-in" ||
"destination-out" || "destination-atop" || "lighter" ||
"copy" || "xor" || "multiply" || "screen" || "overlay" ||
"darken" || "lighten" || "color-dodge" || "color-burn" ||
"hard-light" || "soft-light" || "difference" || "exclusion" ||
"hue" || "saturation" || "color" || "luminosity";
خوشبختانه لازم نیست تمام این مقدارها را بشناسید، هرچند اگر بشناسید ابزار بسیار قدرتمندی در دست خواهید داشت! در این آموزش به توضیح مختصری از هرکدام خواهیم پرداخت و چند مورد مهم از آنها را بررسی خواهیم کرد. برای راحتی کار و شناخت بهتر، در این مقادیر منظور از destination ترسیمات قبلی، و منظور از source ترسیمات جدید است.
source-over
ترسیمات جدید روی ترسیمات قبلی رسم میشوند. (مقدار پیشفرض)source-in
بخشهایی از ترسیمات جدید باقی میمانند که روی ترسیمات قبلی باشند.source-out
بخشهایی از ترسیمات جدید باقی میمانند که روی ترسیمات قبلی نباشند.source-atop
شبیه به source-in ولی ترسیمات دیگر حذف نمیشوند.destination-over
ترسیمات قبلی روی ترسیمات جدید قرار میگیرند.destination-in
بخشهایی از ترسیمات قبلی باقی میمانند که روی ترسیمات جدید قرار میگیرند.destination-out
بخشهایی از ترسیمات قبلی باقی میمانند که روی ترسیمات جدید قرار نمیگیرند.destination-atop
فقط بخشهایی از ترسیمات قبلی باقی میمانند که روی ترسیمات جدید قرار میگیرند؛ و ترسیمات جدید نیز پشت آنها رسم میشوند.copy
فقط ترسیمات جدید باقی میمانند. دیگر ترسیمات حذف میشوند.xor
بخشهایی که ترسیمات قبلی و جدید روی هم قرار میگیرند حذف میشوند. دیگر بخشها به صورت عادی رسم میشوند.lighter
در بخشهایی که ترسیمات قبلی و جدید روی هم قرار میگیرند، مقدارهای رنگ با هم جمع میشوند.multiply
هر پیکسل از ترسیمات جدید با پیکسل ترسیمات قبلی ضرب میشود. نتیجه رنگهای تیرهتر است.screen
برعکس مقدار multiply نتیجه رنگهای روشنتر است.overlay
ترکیبی از multiply و screen که در آن بخشهای تیره، تیرهتر و بخشهای روشن، روشنتر میشوند.darken
تیرهترین رنگها از هرکدام از ترسیمات باقی میمانند.lighten
روشنترین رنگها از هرکدام از ترسیمات باقی میمانند.color-dodge
رنگ زیرین را به معکوس رنگ بالا تقسیم میکند.color-burn
معکوس رنگ زیرین را به رنگ بالا تقسیم میکند.hard-light
شبیه به overlay اما جای ترسیمات جدید و قبلی عوض میشود.soft-light
نسخهی ملایمتری از hard-light که رنگها در آن بیشتر ترکیب میشوند.difference
تفاوت لایهی زیرین را از لایهی بالا استفاده میکند. اگر مقدار منفی شود آن را قرینه میکند.exclusion
شبیه به difference اما با contrast کمترhue
نتیجهی این مقدار یک رنگ HSL است که saturation و luma آن از رنگ قبلی، و hue آن از رنگ جدید است.saturation
شبیه به hue اما با این مقدار، saturation از ترسیمات جدید استفاده میشود.luminosity
شبیه به hue و saturation اما با این مقدار، luma یا رنگ از ترسیمات جدید استفاده میشود.color
برعکس luminosity از رنگ ترسیمات قبلی استفاده شده ولی دیگر مقادیر از ترسیمات جدید استفاده میشوند.darker
این مقدار در نسخههای قدیمی مرورگرها وجود داشت اما به دلایلی دیگر پشتیبانی نمیشود.
همانطور که میبینید، تعداد این مقدایر زیاد بوده و رفتار هرکدام نیز با دیگر مقادیر تفاوت زیادی دارد. از بین این 26 مقدار، چهار مقدار مربوط به source، چهار مقدار مربوط به destination، و دو مقدار xor
و copy
هستند که به ساختار کلی ترسیمات اثر میگذارند. دیگر مقادیر همگی در بازهی رنگها هستند. این موارد میتوانند در ایجاد افکتهای تصویری بسیار کاربردی باشند اما به اندازهی مقدارهای قبلی در همهجا کاربرد ندارند.
ابتدا به ده مقدار اول میپردازیم، زیرا برای دیدن رفتار این ده مقدار نیاز به شکلهای پیچیده نیست. در شکل زیر رفتار این ده مقدار نشان داده شده. توجه کنید که این ده مقدار روی ساختار ترسیمات اثر میگذارند و یادگیری آنها نسبت به دیگر مقادیر مهمتر است. در شکل زیر مستطیل حکم ترسیمات قبلی، و دایره حکم ترسیمات جدید را دارد:
حال به مقدارهای مربوط به رنگ میپردازیم. برای درک بهتر رفتار آنها باید از ترسیماتی استفاده کنیم که دارای طیف رنگ و شفافیت هستند. کد زیر یک تصویر مناسب برای این کار ایجاد میکند. در کد زیر به جای مقدار source-over
مقدارهای متفاوت قرار داده و نتیجه را ببینید. همچنین میتوانید از تصویر پایین کمک بگیرید:
cvs.width = cvs.height = 500;
let g1 = ctx.createRadialGradient(300, 240, 0, 260, 300, 460),
g2 = ctx.createRadialGradient(160, 180, 0, 120, 160, 460);
g1.addColorStop(0.20, "#FC0");
g1.addColorStop(0.05, "#3D0");
g1.addColorStop(0.70, "transparent");
g2.addColorStop(0.05, "#3D0");
g2.addColorStop(0.20, "#3BD");
g2.addColorStop(0.70, "transparent");
ctx.fillStyle = g1;
ctx.arc(260, 300, 460, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = g2;
ctx.arc(120, 160, 460, 0, Math.PI * 2);
ctx.fill();
شاید در نگاه اول بسیار پیچیده به نظر برسند، اما وقتی توضیحات آنها را بخوانید بهتر متوجه کارکرد آنها میشوید. حال که بیشتر و بهتر با این مقدارها آشنا شدیم، زمان آن است که به برخی از کاربردهای آنها اشاره کنیم. در این بخش مروری کوتاه به برخی موارد خواهیم داشت اما در پروژههای آینده با کاربردهای بیشتری آشنا خواهید شد.
پاک کردن لایهی ترسیمات به شکل دلخواه
در موارد بسیاری پیش میآید که بخواهید مساحتی به شکل یک دایره یا چندضلعی را از لایهی ترسیمات پاک کنید. در این موارد، میتوانید از مقدار destination-out
استفاده کنید تا شکل فعلی به جای رسم شدن، لایهی ترسیمات را به شکل خودش پاک کند. به کد زیر توجه کنید. در کد زیر یک مستطیل سبز وارد لایهی ترسیمات میشود، سپس مساحتی به شکل یک دایره لایهی ترسیمات را به کمک این مقدار پاک کرده و بخشی از مستطیل نیز پاک میشود:
ctx.fillStyle = "#3D3";
ctx.fillRect(50, 50, 300, 200);
ctx.globalCompositeOperation = "destination-out";
ctx.arc(160, 200, 130, 0, Math.PI * 2);
ctx.fill();
محدود کردن لایهی ترسیمات بدون نیاز به شکل فعلی
گاهی پیش میآید که میخواهید لایهی ترسیمات را محدود کنید اما بنا به دلایلی نمیتوانید شکل فعلی را به درستی تعیین کرده یا آن را تغییر دهید. در این موقعیت میتوانید از مقدار source-atop
استفاده کنید. با این مقدار میتوانید بدون نیاز به تعیین شکل فعلی و استفاده از متد clip
، ترسیمات بعدی را به محدودهی ترسیمات فعلی درون لایهی ترسیمات محدود کنید. این ویژگی را برای کد بالا امتحان کرده و نتیجه را ببینید. بخشهایی از دایره که خارج از محدودهی ترسیمات قبلی (همان مستطیل) باشند، رسم نمیشوند.
پاک کردن لایهی ترسیمات بدون دردسر
در canvas روشهای زیادی برای پاک کردن لایهی ترسیمات وجود دارد، منتها همگی حداقل یک مشکل همراه خود دارند. سادهترین روش این است که از متد clearRect
استفاده کنیم. اما این متد از متدهای transform تاثیر میپذیرد. با وجود اینکه میتوان متدهای transform را دور زد، اما روش بسیار سادهای برای پاک کردن لایهی ترسیمات وجود دارد و آن استفاده از مقدار copy
است! به کد زیر توجه کنید:
function clear (context) {
let previous_composite = context.globalCompositeOperation,
previous_color = context.fillStyle;
context.globalCompositeOperation = "copy";
context.fillStyle = "rgba(0, 0, 0, 0)";
context.fillRect(0, 0, 1, 1);
context.globalCompositeOperation = previous_value;
context.fillStyle = previous_color;
}
کد بالا یک تابع تعریف میکند که زمینهی موردنظر را به عنوان ورودی دریافت میکند. سپس مقدار فعلی ویژگی globalCompositeOperation
و fillStyle
را ذخیره میکند؛ سپس آن را به copy
تغییر داده و رنگ را نیز کاملا شفاف میکند. حال یک مربع به طول و عرض 1 رسم میکند. سپس مقدار قبلی ویژگیها را پس میدهد. این روش یکی از سادهترین و سریعترین روشها برای پاک کردن ترسیمات canvas است. در آموزشهای آینده توضیحات بیشتری در این باره آورده شده است.
ایجاد توهم سهبعدی
با اینکه در canvas نمیتوان ترسیمات سهبعدی انجام داد، اما اگر مقدار destination-over
را با سایه و رنگ درست ترکیب کنید، میتوانید یک توهم سهبعدی زیبا ایجاد کنید. از این ویژگی در ترسیمات پیشرفته در آینده استفاده خواهد شد.
ایجاد جلوههای تصویری
هرچند روشهای دیگری نیز برای ایجاد جلوه روی تصاویر وجود دارد، اما سرعت پایینی دارند. با توجه به گستردگی مقادیر مربوط به رنگ در این ویژگی، اگر بتوانید ترکیب درستی از ترسیمات را روی تصاویر اعمال کنید، میتوانید جلوههای بسیار سنگین را با کمترین خط کد و با بیشترین سرعت اجرا کنید! در بخشهای آینده بیشتر به این مورد خواهیم پرداخت. فعلا به این مورد کوچک توجه کنید. متغیر img
یک تصویر در صفحه است که میتوانید آن را به دلخواه تعیین کنید:
let img = document.getElementById("an_image");
cvs.width = 1500;
cvs.height = 500;
onload = () => {
img = (() => {
let c = document.createElement("canvas");
c.width = c.height = 500;
c = c.getContext("2d");
c.drawImage(img, 0, 0, 500, 500);
return c.canvas;
}) ();
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 500, 0);
ctx.globalCompositeOperation = "color";
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.fillRect(500, 0, 500, 500);
ctx.drawImage(img, 1000, 0);
ctx.globalCompositeOperation = "color";
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.fillRect(1000, 0, 500, 500);
}
نکات پایانی
جدا از مواردی که بررسی شد، دو نکتهی مهم دربارهی این ویژگی وجود دارد که باید همیشه به آنها توجه داشته باشید.
نوع ترسیمات روی نتیجه اثرگذار هستند
این مورد با اینکه تا حدودی بدیهی است، اما بسیاری آن را فراموش میکنند. گفتیم ویژگی globalCompositeOperation
نوع تعامل ترسیمات جدید را با ترسیمات پیشین تعیین میکند. این یعنی اگر شکل با متد stroke
رسم شود، نتیجهی متفاوتی با متد fill
خواهد داشت. حتی الگوی ترسیمات (ورودی متد fill
) نیز میتوانند نتایج متفاوتی داشته باشند! برای درک بهتر موضوع، در کدهای بالا، به جای متد fill
از متد stroke
استفاده کنید تا بهتر متوجه شوید.
وجود شفافیت در ترسیمات مهم است
در اینجا متوجه میشوید که کاربرد ویژگی globalAlpha
آنقدرها هم کم نیست! شفافیتی که در ترسیمات وجود دارد، روی رفتار globalCompositeOperation
اثرگذار است. در واقع این ویژگی، بسته به میزان شفافیت ترسیمات روی آنها اثر میگذارد. برای نمونه اگر ترسیمات دارای 95% شفافیت باشند، این ویژگی فقط روی 5% غیرشفاف تاثیر میگذارد. برای درک بهتر به کد زیر توجه کنید:
ctx.fillStyle = "rgba(30, 200, 30)";
ctx.fillRect(50, 50, 300, 200);
ctx.fillStyle = "rgba(30, 200, 30, 0.1)";
ctx.arc(160, 200, 130, 0, Math.PI * 2);
cvs.addEventListener("click", () => {
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
});
در کد بالا که مشابه کد قبل برای پاک کردن ترسیمات است، شکل اولیه رسم شده، سپس یک دایره برای پاک کردن لایهی ترسیمات وارد شکل فعلی میشود، نکتهی مهم این است که ویژگی fillStyle
دارای شفافیت 90% است، این یعنی ویژگی globalCompositeOperation
بسته به این شفافیت عمل میکند. با هر بار کلیک روی عنصر cvs
، متد fill
یک بار اجرا میشود.
احتمالا انتظار دارید مانند کد قبل، با یک بار کلیک تمام ترسیمات که زیر دایره قرار میگیرند پاک شوند، اما بعد از امتحان کردن متوجه این ویژگی خواهید شد! همانطور که میبینید، با هر بار کلیک فقط 10% شفافیت ترسیمات کم میشود، علت این است که ترسیمات جدید 90% شفافیت داشتند و این ویژگی روی بخشهای شفاف ترسیمات کار نمیکند. (درست شبیه سایه)
ترکیب شفافیت با این ویژگی (چه به واسطهی فرمت رنگ rgba
، hsla
، یا ویژگی globalAlpha
)، کاربردهای این ویژگی را باز هم بیشتر میکند. استفاده از این ترکیب در انیمیشنها میتواند ترسیمات فوقالعادهای ایجاد کند که بازسازی آنها بدون این ترکیب غیرممکن است!
جدا از تمام مواردی که گفته شد، لازم است اشارهای نیز به دو متد setAlpha
و setCompositeOperation
داشته باشیم. این دو متد در مرورگرهای رده webkit پشتیبانی میشدند اما سالها پیش منقضی شدهاند.
نتیجهگیری
همانطور که تاکنون متوجه شدهاید، این دو ویژگی (برخلاف ظاهرشان) بسیار کاربردی هستند و با کوچکترین تغییر، بیشترین تاثیر را بر ترسیمات میگذارند. پیشنهاد میشود این بخش را به خوبی مطالعه کرده و یاد بگیرید، زیرا این موارد جزء مهمترین ویژگیهای canvas هستند. کاربردهایی که در این بخش بررسی شد، فقط چند مورد جزئی از کاربردهای فراوان این ویژگی (یا ترکیب این دو ویژگی) هستند. در بخش بعدی به تبدیلات (transform) خواهیم پرداخت.
سوال داری؟ برو به پنل پرسش و پاسخ