آغاز کار با canvas و متد‌های اولیه

پس از آشنایی با عنصر canvas و فناوری‌های آن، این نخستین قدم در یادگیری canvas 2D است. از این به بعد به جای canvas 2D از نام canvas استفاده می‌کنیم. این مورد نه‌تنها در اینجا بلکه در تمام وب رعایت می‌شود. دیگر فناوری‌های عنصر canvas نام‌های ویژه‌ی خود یعنی WebGL و WebGPU را دارند. نکته‌ی مهم اینکه شما برای یادگیری canvas باید به جاوااسکریپت تسلط داشته باشید. در این آموزش از ES6 استفاده شده اما تفاوت بزرگی با نسخه‌های پیشین جاوااسکریپت ندارد.

پیش از آغاز از شما خواهشمندیم تمام کد‌ها را بلافاصله بعد از خواندن و یادگیری، آزمایش کنید تا بیشتر و بهتر آن‌ها را یاد بگیرید. حتی می‌توانید به سایت‌هایی مانند JSBin یا JSFiddle بروید و هم‌گام با این آموزش پیش بروید. نتایج کد‌های نوشته‌شده از عمد در این مقاله قرار نگرفته تا خواننده‌ی عزیز ناچار وارد فرآیند یادگیری عملی شود.

راه‌اندازی canvas

برای راه‌اندازی canvas ابتدا به عنصر آن نیاز داریم! توجه کنید که برای استفاده از عنصر و ایجاد ترسیمات روی آن باید عنصر را در جاوااسکریپت نیز انتخاب کنیم، بنابراین به آن یک شناسه یا id مناسب می‌دهیم. درضمن همانطور که گفتیم چیزی درون این عنصر قرار نمی‌گیرد. پیش از این، متنی درون این عنصر نوشته می‌شد که در صورت عدم پشتیبانی مرورگر از canvas آن متن نمایش داده شود ولی از آن دوران خیلی وقت است که گذشته!


<canvas id="cvs"></canvas>

شاید نیازی به گفتن این مورد نباشد اما توجه کنید که جاوااسکریپت پس از آنکه سند DOM بارگیری شد، اقدام به انتخاب عنصر می‌کند. برای حل این مشکل احتمالی می‌توانید ویژگی defer را به عنصر script اضافه کنید. حال در جاوااسکریپت این عنصر را انتخاب می‌کنیم:


let cvs = document.getElementById("cvs");

عنصر canvas یک متد خاص به نام getContext دارد که با استفاده از آن می‌توان یک فناوری خاص را برای آن عنصر فعال کرد و یک «زمینه» (context) برای کار با آن فناوری دریافت کرد. این متد یک ورودی از نوع متن دریافت می‌کند که نوع زمینه‌ی خروجی را مشخص می‌کند. زمینه‌ی موردنظر ما دوبعدی است و برای دریافت آن، ورودی متد باید “2d” باشد. در زیر ورودی‌های دیگر نیز بررسی شده‌اند:


let ctx = cvs.getContext("2d"), /* CanvasRenderingContext2D */
	
    gl = cvs.getContext("webgl"), /* WebGLRenderingContext */
    gl2 = cvs.getContext("webgl2"), /* WebGL2RenderingContext */
    
    gpu = cvs.getContext("webgpu"); /* WebGPURenderingContext */

نکته‌ی بسیار مهم اینکه برای هر عنصر canvas فقط و فقط می‌توان یک زمینه ساخت. یعنی اگر برای یک عنصر canvas ابتدا زمینه‌ی 2D ایجاد کرده باشید، و سپس سعی کنید یک زمینه‌ی WebGL نیز از آن بگیرید مقدار null خواهید گرفت! برای نمونه سه خط آخر کد بالا هر سه مقدار null برمی‌گردانند چون ابتدا یک زمینه از نوع 2D برای عنصر cvs ساخته شده بود.

حال این «زمینه» به چه دردی می‌خورد؟ تمام ترسیمات توسط این زمینه‌ها انجام می‌شود! عنصر canvas از این به بعد شبیه عنصر img رفتار می‌کند و تمام تمرکز ما روی زمینه و ویژگی‌های آن خواهد بود. فعلا برای راحتی کار و برای مثال‌های آینده، کد ابتدایی به این صورت خواهد بود:


let cvs = document.getElementById("cvs"),
    ctx = cvs.getContext("2d");

cvs.width = cvs.height = 500;

عنصر canvas به صورت پیش‌فرض دارای ابعاد 300 در 150 است ولی در کد بالا اندازه‌ی آن را به 500 در 500 تغییر دادیم. توجه کنید تعیین ویژگی width و height برای عنصر canvas در CSS نتیجه‌ی متفاوتی خواهد داشت که بعدا توضیح خواهیم داد.

اولین گام، مستطیل!

با استفاده از متد rect می‌توان یک مستطیل رسم کرد. این متد چهار ورودی می‌پذیرد که به ترتیب مختصات X، مختصات Y، طول و عرض هستند (مختصات canvas شبیه مختصات SVG است). برای نمونه در کد زیر یک مربع به طول و عرض 100 و در مختصات (10, 10) رسم می‌کنیم:


ctx.rect(10, 10, 100, 100); /* rect (x, y, width, height) */

بعد از اجرای کد متوجه می‌شوید که چیزی درون canvas رسم نشده! علت این است که ما شکل موردنظر برای رسم را تعریف کردیم، ولی این برای رسم کافی نیست! باید رنگ شکل، نوع رسم و دیگر موارد نیز مشخص شوند و سپس دستور رسم صادر شود!

رسم شکل در canvas

در canvas دو متد برای رسم داریم. یکی متد fill که تمام شکل را رنگ می‌کند، و دیگری متد stroke که به شکل حاشیه می‌دهد. این متد‌ها معمولا هیچ ورودی‌ای دریافت نمی‌کنند. متد fill را برای کد بالا امتحان می‌کنیم. کد به صورت زیر خواهد بود:


/* FILL RECT */
ctx.rect(10, 10, 100, 100);
ctx.fill();

نتیجه یک مربع سیاه است. ولی شاید شما یک مربع سبز یا آبی بخواهید! برای تغییر رنگ ویژگی fillStyle را تغییر می‌دهیم. برای نمونه کد زیر یک مربع سبز رسم می‌کند:


ctx.rect(10, 10, 100, 100);

ctx.fillStyle = "#32CD32"; /* GREEN */
ctx.fill();

حال ممکن است از خود بپرسید «اگر ویژگی fillStyle را بعد از ترسیم عوض کنیم چه اتفاقی می‌افتد؟» هیچ اتفاقی! پس از اینکه یک شکل رسم شد، تغییر این ویژگی‌ها هیچ تاثیری بر آن نخواهد داشت، مگر اینکه آن شکل مستقیما پاک شود. این یکی از تفاوت‌های اصلی canvas و SVG است.

البته توجه کنید در مثال بالا، حتی بعد از اینکه دستور fill اجرا شد، هنوز هم یک مربع به مختصات (10, 10) و طول ضلع 100 در حافظه‌ی canvas وجود دارد؛ یعنی اگر به fillStyle رنگ قرمز بدهیم و دوباره دستور fill را اجرا کنیم، یک مربع قرمز درست در همان مختصات و به همان اندازه رسم می‌شود و به نظر می‌رسد که مربع سبز تغییر رنگ داده! این حافظه‌ی canvas که به آن اشاره کردیم، «شکل فعلی» (current path) نامیده می‌شود و کاربرد‌های فراوانی دارد که بعدها به آن‌ها خواهیم پرداخت.

آیا تغییر رنگ حاشیه‌ی شکل نیز ممکن است؟ بله! هر رنگی که به ویژگی strokeStyle بدهید، حاشیه‌ی شکلی که رسم می‌شود به آن رنگ خواهد بود. برای نمونه در کد بالا این ویژگی را امتحان کرده و به مربع حاشیه بدهید. نکته‌ی مهم دیگر اینکه هر نوع رنگی که در CSS قابل قبول باشد را می‌توان برای fillStyle و strokeStyle نیز به کار برد. در زیر چند نمونه می‌بینید:


ctx.fillStyle = "#000"; /* SHORT HEX */
ctx.fillStyle = "#008DDE"; /* LONG HEX */

ctx.strokeStyle = "rgb(255, 255, 0)"; /* RGB */
ctx.strokeStyle = "rgba(0, 100, 20, 1)"; /* RGBA */

ctx.fillStyle = "hsl(60, 100%, 50%)"; /* HSL */

شاید بخواهید اندازه‌ی حاشیه را تغییر دهید. ویژگی lineWidth در خدمت شماست! این ویژگی مقدار عددی می‌پذیرد و بهتر است عدد صحیح باشد. برای نمونه کد زیر یک مستطیل در مختصات (100, 50) و به ابعاد 300 در 200 رسم می‌کند. رنگ داخل آن صورتی، حاشیه‌ی آن 10 و به رنگ زرد است:


ctx.rect(100, 50, 300, 200);
ctx.fillStyle = "deeppink";

ctx.lineWidth = 10;
ctx.strokeStyle = "#FF0";

ctx.fill();
ctx.stroke();

حاشیه‌ی شکل (چه در canvas و چه در SVG) به این صورت است که نیمی از آن داخل شکل و نیمی از آن بیرون شکل رسم می‌شود. از آنجایی که متد stroke بعد از متد fill فراخوانی شد، نیمی از حاشیه داخل شکل رسم می‌شود. در حالت عادی آخرین چیزی که در canvas رسم شده باشد، روی دیگر ترسیمات قرار می‌گیرد؛ یعنی اگر در کد بالا، ابتدا متد stroke و سپس متد fill اجرا شود، ابتدا حاشیه‌ی شکل رسم می‌شود و سپس رنگ شکل روی نیمه‌ی داخلی حاشیه قرار می‌گیرد و حاشیه کوچکتر به نظر می‌رسد.

برای درک بهتر این موضوع، در کد بالا به lineWidth مقداری بزرگ مانند 50 بدهید و جای متد‌های fill و stroke را عوض کرده و نتایج را مقایسه کنید. همچنین شکل زیر این موضوع را بهتر نمایش می‌دهد:

canvas and SVG stroke

هر سه این مربع‌ها به یک اندازه هستند اما اندازه‌ی حاشیه lineWidth هر کدام متفاوت است. (به خط سیاه داخل هرکدام توجه کنید) این مورد در حاشیه‌های کوچک مشکل خاصی نیست اما در حاشیه‌های بزرگ (مانند مربع زرد با حاشیه‌ی 40) باید این مورد را در نظر داشته باشید.

نتیجه‌گیری

در اولین آموزش از canvas شما یاد گرفتید که چطور یک زمینه برای عنصر canvas تعریف کرده و ویژگی‌ها و متد‌های اولیه برای ترسیم را یاد گرفتید. سعی کنید پیش از خواندن آموزش بعدی با این ویژگی‌ها و متد‌ها به اندازه کار کنید تا به خوبی آن‌ها را یاد بگیرید. در آموزش بعدی متد‌های مربوط به اشکال اولیه و خطوط را خواهید آموخت.

حسین رفیعی

حسین رفیعی

طراحی و برنامه‌نویسی رو از وب شروع کردم و مثل خیلی از شماها آموزش‌های آقای سیدی خیلی بهم کمک کرد. هرچند این روزا تمرکز من روی برنامه‌نویسی خارج از وب هست ولی هنوز هم توی این فضا هستم و امیدوارم بتونم به بقیه کمک کنم!

سوال داری؟ برو به پنل پرسش و پاسخ

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *