آموزش رابط Canvas HTML
Canvas یک تگ HTML (< canvas >) است که از طریق آن میتوانیم با استفاده از Canvas API به طراحی و نقاشی بپردازیم.
ایجاد Canvas
انجام این کار بسیار ساده است و تنها کافی است که < canvas >< /canvas > را داخل یک فایل HTML خالی بی اندازید.
در حال حاضر در این صفحه چیزی نمیبینید، چرا که این canvas یک عنصر نامرئی است. مقداری حاشیه به آن اضافه کنید.
کروم بهصورت خودکار به عنصر body یک حاشیهی 8 پیکسلی اضافه میکند. به همین خاطر است که حاشیهی ما شبیه به یک کادر شده است. با تنظیمات زیر میتوانید حاشیهی کروم را حذف کنید.
body {
margin: 0;
}
فعلاً کاری به تنظیمات پیشفرض نداریم.
حالا میتوانیم از طریق جاوا اسکریپت و با استفاده از DOM Selectors API به canvas خود دسترسی پیدا کنیم. بنابراین میتوانیم از document.querySelector() استفاده کنیم.
const canvas = document.querySelector('canvas')
تغییر رنگ پسزمینهی canvas
این کار در CSS انجام میشود:
canvas {
background-color: lightblue;
}
تغییر اندازهی canvas
میتوانید از CSS عرض و ارتفاع canvas را تنظیم کنید.
canvas {
border: 1px solid black;
width: 100%;
height: 100%;
}
و از این طریق canvas تا حدی بزرگ میشود که کل اندازهی عنصر خارجی را پر کند.
اگر canvas خود را بهعنوان یک عنصر سطح اول در HTML قرار دهید، در این صورت کد بالا باعث میشود که این canvas تا حدی که کل بدنه را پر کند، بزرگ شود.
در حال حاضر بدنه، کل اندازهی پنجره را پر نکرده است. برای اینکه کل صفحه پر شود، باید از جاوا اسکریپت استفاده کنیم.
canvas.width = window.innerWidth
canvas.height = window.innerHeight
حالا اگر حاشیهی بدنه را حذف کنید و پسزمینهی canvas را با استفاده از CSS تنظیم کنید، canvas کل صفحه را پر میکند و میتوانیم بر روی آن طراحی و نقاشی کنیم.
اگر اندازهی پنجره عوض شد، ما باید عرض canvas را هم مجدداً محاسبه کنیم که این کار جهت جلوگیری از فراخوانی رویداد تغییر اندازهی canvas با استفاده از debounce انجام میشود (رویداد resize را با هر بار حرکت پنجره از طریق موس، میتوان صدها بار فراخوانی کرد)، برای مثال:
const debounce = (func) => {
let timer
return (event) => {
if (timer) { clearTimeout(timer) }
timer = setTimeout(func, 100, event)
}
}
window.addEventListener('resize', debounce(() => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}))
دریافت زمینه از canvas
برای ترسیم در canvas باید یک زمینه داشته باشیم:
const c = canvas.getContext('2d')
برخی زمینه را به متغیری به نام c و برخی دیگر به ctx تخصیص میدهند، که هر دوی آنها روشی رایج برای مخفف کردن زمینه (context) است.
متد getContext() یک زمینهی طراحی را در canvas برگشت میدهد که این کار بر اساس نوع پارامتر عبوری مشخص میشود.
مقادیر معتبر را میتوانید در زیر مشاهده کنید:
- 2d: مقداری که ما از آن استفاده خواهیم کرد.
- webgl : جهت استفاده از نسخهی یک WebGL کاربرد دارد.
- webgl2 : جهت استفاده از نسخهی دو WebGL کاربرد دارد.
- bitmaprenderer : از آن میتوان در کنار ImageBitmap استفاده کرد.
بر اساس نوع زمینه میتوانید پارامتر دومی را نیز به getContext() بدهید تا مشخصات بیشتری را تعیین کنید.
برای زمینهی 2d ما اساساً یک پارامتر داریم و میتوانیم از آن در تمامی مرورگرها استفاده کنیم. این پارامتر alpha نام دارد، یک پارامتر بولی است و مقدار آن بهصورت پیشفرض true است. اگر مقدار آن بر روی false تنظیم شود، مرورگر میفهمد که canvas پسزمینهی شفافی ندارد و بنابراین میتواند سرعت رندر را افزایش دهد.
ترسیم عناصر در canvas
حالا با کمک زمینههای بالا میتوانیم عناصر مورد نظر خود را ترسیم کنیم.
روشهای زیادی برای انجام این کار وجود دارد که میتوانیم با کمک آنها عناصر زیر را ترسیم کنیم:
- متن
- خطوط
- مستطیل
- مسیر
- تصویر
و میتوانیم fill، stroke، gradient، pattern و shadow هر یک از آنها را تغییر دهیم. همچنین میتوانیم آنها را بچرخانیم، مقیاس آنها را تغییر دهیم و ... .
بیایید کار خود را با یک مستطیل که سادهترین عنصر است شروع کنیم. برای انجام این کار از متد fillRect(x, y, width, height) استفاده میکنیم.
c.fillRect(100, 100, 100, 100)
این کار باعث میشود یک مستطیل سیاهرنگ 100 پیکسل در 100 پیکسل کشیده شود که موقعیت افقی و عمودی آن هر دو 100 است.
با استفاده از متد fillStyle() میتوانید این مستطیل را رنگ کنید. برای انجام این کار تنها کافی است یکی از رشتههای رنگ معتبر CSS را در این متد عبور دهید.
c.fillStyle = 'white'
c.fillRect(100, 100, 100, 100)
حالا میتوانید با کمک خلاقیت خود، چیزهای بسیاری را ترسیم کنید.
for (let i = 0; i < 60; i++) {
for (let j = 0; j < 60; j++) {
c.fillStyle = `rgb(${i * 5}, ${j * 5}, ${(i+j) * 50})`
c.fillRect(j * 20, i * 20, 10, 10)
}
}
یا
for (let i = 0; i < 60; i++) {
for (let j = 0; j < 60; j++) {
c.fillStyle = `rgb($ {i * 5}, $ {j * 5}, $ {(i+j) * 50})`
c.fillRect(j * 20, i * 20, 20, 20)
}
}
ترسیم عناصر
همانطور که قبلاً نیز اشاره کردیم، میتوانید عناصر زیر را ترسیم کنید:
- متن
- خطوط
- مستطیل
- مسیر
- تصویر
صرفاً برای آنکه کلیت کار مشخص شود، به مستطیل و متن میپردازیم.
تغییر رنگ
جهت تغییر fill و stroke رنگها و اشکال میتوانید از مشخصههای fillStyle و strokeStyle استفاده کنید. این مشخصهها تمامی رنگهای معتبر CSS ازجمله رشتهها و اعداد RGB را میپذیرند.
c.strokeStyle = `rgb(255, 255, 255)`
c.fillStyle = `white`
c.strokeStyle = `rgb(255, 255, 255)`
c.fillStyle = `white`
مستطیل
- clearRect(x, y, width, height)
- fillRect(x, y, width, height)
- strokeRect(x, y, width, height)
در بخش قبل با fillRect() آشنا شدیم. strokeRect() نیز از نظر اسم شبیه به آن است اما بهجای پر کردن مستطیل، صرفاً با استفاده از سبک stroke فعلی، stroke مستطیل را ترسیم میکند (که آن را میتوان با استفاده از ویژگی زمینهی strokeStyle تغییر داد).
const c = canvas.getContext('2d')
for (let i = 0; i < 61; i++) {
for (let j = 0; j < 61; j++) {
c.strokeStyle = `rgb($ {i * 5}, $ {j * 5}, $ {(i+j) * 50})`
c.strokeRect(j * 20, i * 20, 20, 20)
}
}
clearRect() پسزمینهی ناحیهای را حذف میکند.
متن
نوشتن متن شبیه به ترسیم مستطیل انجام میشود.
برای انجام این کار از دو روش زیر میتوانید استفاده کنید:
- fillText(text, x, y)
- strokeText(text, x, y)
با استفاده از این دو روش میتوانید متن خود را در canvas بنویسید.
X و y به پایین گوشه سمت چپ اشاره دارد.
میتوانید خانوادهی فونت و اندازه را با استفاده از ویژگی font مربوط به canvas تغییر دهید.
c.font = '148px Courier New'
ویژگیهای مربوط به متن دیگری نیز هستند که میتوانید آنها را تغییر دهید (* = پیشفرض):
- textAlign (start*, end, left, right, center)
- textBaseline (top, hanging, middle, alphabetic*, ideographic, bottom)
- direction (ltr, rtl, inherit*)
خطوط
برای آنکه بتوانید خطی را بکشید، ابتدا باید متد beginPath() را فراخوانی کنید، پس از آن با استفاده از moveTo(x, y) نقطهی آغازینی را مشخص کنید و سپس برای قرار دادن این خط بر روی مختصات جدید lineTo(x, y) را فراخوانی کنید و در نهایت stroke() را فراخوانی کنید.
c.beginPath()
c.moveTo(10, 10)
c.lineTo(300, 300)
c.stroke()
این خط بر اساس مقدار ویژگی c.strokeStyle رنگ خواهد شد.
مثال پیچیدهتر
این کد canvas ای را ایجاد میکند که حاوی 800 دایره است.
هر دایره بهخوبی در این canvas قرار گرفته و شعاع آن بهصورت تصادفی انتخاب شده است.
هر زمان که بخواهید اندازهی این پنجره را تغییر دهید، این عناصر مجدداً تولید میشوند.
در Codepen میتوانید با آن بازی کنید.
const canvas = document.querySelector('canvas')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const c = canvas.getContext('2d')
const circlesCount = 800
const colorArray = [
'#046975',
'#2EA1D4',
'#3BCC2A',
'#FFDF59',
'#FF1D47'
]
const debounce = (func) => {
let timer
return (event) => {
if (timer) { clearTimeout(timer) }
timer = setTimeout(func, 100, event)
}
}
window.addEventListener('resize', debounce(() => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
init()
}))
const init = () => {
for (let i = 0; i < circlesCount; i++) {
const radius = Math.random() * 20 + 1
const x = Math.random() * (innerWidth - radius * 2) + radius
const y = Math.random() * (innerHeight - radius * 2) + radius
const dx = (Math.random() - 0.5) * 2
const dy = (Math.random() - 0.5) * 2
const circle = new Circle(x, y, dx, dy, radius)
circle.draw()
}
}
const Circle = function(x, y, dx, dy, radius) {
this.x = x
this.y = y
this.dx = dx
this.dy = dy
this.radius = radius
this.minRadius = radius
this.color = colorArray[Math.floor(Math.random() * colorArray.length)]
this.draw = function() {
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
c.strokeStyle = 'black'
c.stroke()
c.fillStyle = this.color
c.fill()
}
}
init()
مثال دیگر: متحرکسازی عناصر در canvas
بر اساس مثال بالا میتوانیم با استفاده از یک حلقه این عناصر را متحرک کنیم. هر دایره عمر مخصوص به خود را دارد و داخل حاشیههای canvas حرکت میکند. زمانی که دایره به حاشیه برخورد میکند، مسیرش عوض میشود.
این کار از طریق requestAnimationFrame() انجام میشود و در هر فریم با رندر کردن حلقهی تکرار تصویر را کمی حرکت میدهد.
تعامل با عناصر در canvas
مثال بالا در ادامه گسترش داده شده است تا شما بتوانید از طریق موس با دایرهها تعامل برقرار کنید.
زمانی که موس را در canvas حرکت میدهید، دایرههای نزدیک موس شما بزرگ میشوند و زمانی که از آنها فاصله میگیرید، بهاندازهی اصلی خود برمیگردند.
شیوهی کارکرد این قابلیت به چه صورت است؟ در واقع کار ردیابی موقعیت موس از طریق دو متغیر انجام میشود.
let mousex = undefined
let mousey = undefined
window.addEventListener('mousemove', (e) => {
mousex = e.x
mousey = e.y
})
سپس از این متغیرها در متد update() مربوط به Circle استفاده میکنیم تا مشخص شود که شعاع باید افزایش یابد یا کاهش.
if (mousex - this.x < distanceFromMouse && mousex - this.x > -distanceFromMouse && mousey - this.y < distanceFromMouse && mousey - this.y > -distanceFromMouse) {
if (this.radius < maxRadius) this.radius += 1
} else {
if (this.radius > this.minRadius) this.radius -= 1
}
distanceFromMouse مقداری برحسب پیکسل است (در اینجا برابر با 200 است) که مشخص میکند تا چه فاصلهای دایرهها باید به موس واکنش نشان دهند.
عملکرد
اگر میخواهید پروژههای بالا را ویرایش کنید و دایرهها و قطعات متحرک بیشتری را به آنها اضافه کنید، در این صورت باید حواستان به مشکلات عملکرد باشد. مرورگرها جهت رندر کردن این canvas همراه با انیمیشنها و تعامل انرژی زیادی مصرف میکنند. بنابراین باید حواستان باشد که این تجربه در دستگاههای با عملکرد ضعیفتر از بین نرود.
برای مثال من زمانی که میخواستم بهجای دایره همین کار را با ایموجی انجام دهم، فهمیدم که رندر شدن متن توان زیادی میطلبد و به همین دلیل این انیمیشن بهسرعت کند شد.
در این لینک میتوانید فهرستی از نکات عملکردی را مشاهده کنید.
حرف آخر
در اینجا به معرفی امکانات canvas که یک ابزار شگفتانگیز جهت ایجاد تجارب خارقالعاده در صفحات اینترنتی است پرداختیم.