فهرست
تابع جاوااسکریپت چیه؟
میخوام با سادهترین تعریفی که میتونم از تابع جاوااسکریپت ارائه بدم شروع کنم: یه تابع، مجموعهای از کُدهاس که یک یا چند عملیات بهخصوص انجام میده. اصلاً نگران نباشید، قراره همه چی براتون کاملاً روشن بشه و بعد که یاد گرفتین چطوری از تابعها استفاده کنید، قراره کلی کارهای باحال انجام بدید!
تابعها اساسیترین عناصر همه برنامههای p5.js هستن. وقتی یه تابع ساخته میشه، دیگه لازم نیست کدی که داخل اون تابع نوشته شده دوباره جای دیگهای تکرار بشه، بلکه کافیه از همون تابع استفاده بشه. تا حالا توی خیلی از مثالها از تابعهای آمادهای مثل createCanvas
و fill
استفاده کردیم. علاوه بر تابعهای آمادهای که p5.js در اختیارمون قرار میده، خودمون هم میتونیم تابع بنویسیم و توی برنامهمون ازش استفاده کنیم.
قبل از هر چیز میخوام از تابعهای آمادهی p5.js استفاده کنم و یه جغد بکشم:
function setup() {
createCanvas(windowWidth, windowHeight)
}
function draw() {
background(204)
translate(110, 175)
stroke(0)
strokeWeight(70)
line(0, -35, 0, -65)
noStroke()
fill(255, 255, 255)
ellipse(-17.5, -65, 35, 35)
ellipse(+17.5, -65, 35, 35)
arc(0, -65, 70, 70, 0, PI)
fill(0)
ellipse(-14, -65, 8, 8)
ellipse(+14, -65, 8, 8)
quad(0, -58, 4, -51, 0, -44, -4, -51)
}
فعلاً خیلی با جزییات این کد کاری نداشته باشید و فقط این کد رو کپی کنید و نتیجه رو ببینید. دقت کنید که من از translate
استفاده کردم؛ این تابع نقطه صفر و صفر بوم شما رو از بالا سمت چپ جابجا میکنه و به نقطه جدیدی که شما تعیین میکنید تغییر میده؛ از این جا به بعد، همه مختصاتی که وارد میکنید نسبت به این نقطه صفر و صفرِ جدید تعیین میشه.
کدی که الان نوشتیم تا این جغد به وجود بیاد هیچ مشکلی نداره و خیلی هم منطقی به نظر میرسه؛ اما اگه قرار باشه به جای این یه دونه جغد، دو تا جغد خوشگل داشته باشیم، اون وقت اندازه کُدمون دو برابر میشه؛ ببینید:
function setup() {
createCanvas(windowWidth, windowHeight)
}
function draw() {
background(204)
// owl one
translate(110, 175)
stroke(0)
strokeWeight(70)
line(0, -35, 0, -65)
noStroke()
fill(255, 255, 255)
ellipse(-17.5, -65, 35, 35)
ellipse(+17.5, -65, 35, 35)
arc(0, -65, 70, 70, 0, PI)
fill(0)
ellipse(-14, -65, 8, 8)
ellipse(+14, -65, 8, 8)
quad(0, -58, 4, -51, 0, -44, -4, -51)
// owl two
translate(70, 0)
stroke(0)
strokeWeight(70)
line(0, -35, 0, -65)
noStroke()
fill(255, 255, 255)
ellipse(-17.5, -65, 35, 35)
ellipse(+17.5, -65, 35, 35)
arc(0, -65, 70, 70, 0, PI)
fill(0)
ellipse(-14, -65, 8, 8)
ellipse(+14, -65, 8, 8)
quad(0, -58, 4, -51, 0, -44, -4, -51)
}
به غیر از background
که مربوط به رنگ پسزمینه بود، من همه خطوط رو کپی کردم و برای جغد جدید translate
رو تغییر دادم تا کنار جغد قبلی قرار بگیره؛ درسته که داره کار میکنه، ولی این کد منطقی و بهینه نیست؛ چون تعداد زیادی خط تکراری توی کد وجود داره و این اصلاً جذاب نیست. از طرفی، فرض کنید به جای دو تا جغد قرار بود مثلاً پونزده تا جغد داشته باشیم؛ این جور مواقع باید چی کار کنیم؟
باید یه تابع بنویسیم!
توی مثال بعد یه تابع مینویسیم که برامون جغد بکشه. این طوری لازم نیست هر بار که به یه جغد نیاز داشتیم این همه خط کد بنویسیم یا کپیپیست کنیم:
function setup() {
createCanvas(windowWidth, windowHeight)
}
function draw() {
background(204)
owl(110, 175)
owl(70, 0)
owl(70, 0)
}
function owl(x, y) {
translate(x, y)
stroke(0)
strokeWeight(70)
line(0, -35, 0, -65)
noStroke()
fill(255, 255, 255)
ellipse(-17.5, -65, 35, 35)
ellipse(+17.5, -65, 35, 35)
arc(0, -65, 70, 70, 0, PI)
fill(0)
ellipse(-14, -65, 8, 8)
ellipse(+14, -65, 8, 8)
quad(0, -58, 4, -51, 0, -44, -4, -51)
}
با کلیدواژه function
یه تابع ساختم و اسمش رو owl
گذاشتم؛ توی پرانتزی که جلوش باز و بسته شده، دو تا پارامتر براش در نظر گرفتم که بتونم با هر بار فراخوندن تابع owl
مختصات جغد جدیدو تغییر بدم، تا جایی که دوست دارم قرار بگیره. پارامترها بخش خیلی مهمی از تابعها هستن، چون باعث میشه بتونیم از یه تابع ثابت، خروجیهای متغیر بگیریم؛ مثلاً اگه میخواستیم میتونستیم رنگ و ابعاد جغدها رو هم با همین یک تابع تغییر بدیم؛ بعد از این که تابع رو درست کردم، اون رو سه بار توی تابع draw
فراخوندم. میبینید؟ با این که این بار سه تا جغد داریم، اما کدمون کوتاهتر و تمیزتر از کد قبلی شده؛ این طوری بهتر نیست؟
پس برای ساختن یه تابع باید این کارها رو انجام بدیم:
- از کلیدواژه
function
استفاده میکنیم - اسم تابع رو انتخاب میکنیم و مینویسیم
- توی پرانتز جلوش، پارامترها رو مینویسیم (اگه تابع پارامتری نداشت، پرانتزها خالی میمونن)
- توی براکت جلوش، کدی که قراره با فراخونده شدن تابع اجرا بشه رو مینویسیم
- هر جایی که خواستیم، با استفاده از اسمِ تابع، تابع رو فراخونی میکنیم و پارامترهاشو مینویسیم
برگردوندن «مقدار»
یه کاری که تابعها میتونن انجام بدن و برنامهنویسها زیاد ازش استفاده میکنن اینه که یه محاسباتی رو انجام بدن و در نهایت یه «مقدار»ی رو برگردونن. مثلاً p5.js یه تابع آمادهای داره به نام random
که وقتی فراخونی میشه به صورت تصادفی یه عدد به ما برمیگردونه و ما میتونیم این عدد تصادفی رو داخل یه متغیر ذخیره کنیم (اگه نمیدونید متغیر چیه باید یه نگاهی به مطلب قبلی که درباره متغیرها نوشتم بندازید):
const randomNumber = random(1, 10)
اگه مثل من به تولید تصاویر جِنِریتیو علاقهمند باشید باید بدونید که قراره خیلی از این تابع random
استفاده کنید، چون اساساً هنر مولد و تصاویر جِنِریتیو معمولاً بر اساس مقادیر تصادفی به وجود میان. به شکلهای مختلف میشه از تابع random
استفاده کرد؛ مثلا:
- اگه هیچ عددی به عنوان پارامتر بهش ندید: یه عدد تصادفی بین صفر و یک برمیگردونه
- اگه یک عدد به عنوان پارامتر بهش بدید: یه عدد تصادفی بین صفر و اون عدد برمیگردونه
- اگه دو تا عدد به عنوان پارامتر بهش بدید: یه عدد تصادفی بین اون دو عدد برمیگردونه
البته اینایی که گفتم همه قابلیتهای این تابعِ شگفتانگیز نیست؛ حتماً صفحه مربوط به تابع random
توی رفرنس p5.js رو بخونید تا اطلاعات کاملی به دست بیارید.
چطوری خودمون یه تابع بسازیم که «مقدار» برگردونه؟
برای این کار باید پارامترهایی که نیاز داریمو مشخص کنیم، کدی که قراره محاسبه رو انجام بده بنویسیم و در نهایت از کلیدواژه return
استفاده کنیم تا مقدار مد نظرمون رو به دست بیاریم:
function setup() {
console.log(sum(3, 4, 5))
// 12
}
function draw() {
}
function sum(a, b, c) {
return a + b + c
}
یه تابع اگه فراخونده نشه اثری روی برنامه نداره؛ در واقع تابعها بعد از تعریف شدن حتماً باید یه جایی استفاده بشن، وگرنه تعریف شدنشون بیفایده بوده. ممکنه براتون عجیب باشه که چرا تابعهای setup
و draw
که توی برنامههای p5.js وجود دارن، بدون فراخونده شدن اجرا میشن؟ این به خاطر معماری این کتابخونهس؛ به دلایلی سازندههای p5.js تصمیم گرفتن که این طوری کار کنه و توی رفرنس رسمی p5.js هم نوشته شده که تابعهای setup
و draw
نباید جایی از برنامه فراخونده بشن.
چرخش و جابجا کردن شکلها
توی مثال بعد میخوام از تابعهای آماده p5.js استفاده کنم و یه شکلی رو بچرخونم؛ ببینید این کار چطوری انجام میشه:
function setup() {
createCanvas(windowWidth, windowHeight)
rectMode(CENTER)
angleMode(DEGREES)
noStroke()
}
function draw() {
background(220)
fill(237, 34, 93)
// rotating the shape
translate(windowWidth/2, windowHeight/2)
rotate(45)
rect(0, 0, 100, 100)
}
از پارامتر CENTER
توی تابع rectMode
استفاده کردم تا مشخص کنم مربع باید از وسط شروع به کشیده شدن کنه؛ توی p5.js مستطیلها و مربعها به صورت پیشفرض از گوشه خودشون شروع به کشیدهشدن میکنن، برای همین من از rectMode
استفاده کردم تا مربع از وسط خودش کشیده بشه.
همچنین از پارامتر DEGREES
توی تابع angleMode
استفاده کردم تا مشخص کنم زاویهها با چه واحدی اندازهگیری بشن؛ توی p5.js زاویهها به صورت پیشفرض از واحد رادیان استفاده میکنن، که من اونو به درجه یا DEGREES
تغییر دادم.
از تابع translate
استفاده کردم تا نقطه مبدأ (یا همون صفر و صفر) رو به وسط صفحه منتقل کنم؛ این یعنی وقتی یه شکل کشیده بشه، اون شکل در وسط صفحه قرار میگیره. بعد از تابع rotate
استفاده کردم تا شکل رو بچرخونم و در نهایت، خود مربع رو کشیدم؛ دقت کنید همون طوری که قبلتر اشاره کردم، تابع translate
نقطه صفر و صفر بوم شما رو از بالا سمت چپ جابجا میکنه و به نقطه جدیدی که شما تعیین میکنید تغییر میده؛ از این جا به بعد، همه مختصاتی که وارد میکنید نسبت به این نقطه صفر و صفر جدید تعیین میشه؛ برای همینه که تابع rotate
بعد از تابع translate
فراخونی شده، چون چرخش باید نسبت به مبدأ مختصات جدید انجام بشه وگرنه نتیجهای که مورد نظر ما بوده به دست نمیاد؛ برای این که بهتر متوجه بشید، فرض کنید میخوایم چهار تا مربع جدید اطراف این مربع بکشیم. دیگه لازم نیست مربعهای بعدی رو بچرخونید، بلکه فقط کافیه مختصات مربعهای جدید رو نسبت به مبدأ مختصات مشخص کنید:
function setup() {
createCanvas(windowWidth, windowHeight)
rectMode(CENTER)
angleMode(DEGREES)
noStroke()
}
function draw() {
background(220)
fill(237, 34, 93)
// rotating the shape
translate(windowWidth/2, windowHeight/2)
rotate(45)
rect(0, 0, 100, 100)
rect(110, 0, 100, 100)
rect(0, 110, 100, 100)
rect(0, -110, 100, 100)
rect(-110, 0, 100, 100)
}
پوش و پاپ
حالا اگه دوست نداشتیم این طور باشه چی؟ یعنی بخوایم یه بخش از کد، فارغ از تنظیمات بخشهای دیگه کار کنه چی؟ در این صورت باید اون بخش رو بین تابعهای push
و pop
بنویسیم؛ این طوری برای کدی که بین این دو تابع نوشته میشه یه وضعیت جدید به وجود میاریم؛ دقت کنید که این دو تابع هم از توابع آماده p5.js هستن:
function setup() {
createCanvas(windowWidth, windowHeight)
rectMode(CENTER)
angleMode(DEGREES)
noStroke()
}
function draw() {
background(220)
fill(237, 34, 93)
// rotating the shape
push()
translate(windowWidth/2, windowHeight/2)
rotate(45)
rect(0, 0, 100, 100)
pop()
// new squares
rect(110, 0, 100, 100)
rect(0, 110, 100, 100)
rect(0, -110, 100, 100)
rect(-110, 0, 100, 100)
}
همون طور که میبینید، تنظیماتی که بین تابعهای push
و pop
نوشته شده، روی مربعهای دیگه اعمال نشده و اصطلاحاً این بخش از کد، ایزوله شده. تابعهای push
و pop
همیشه با همدیگه استفاده میشن؛ استفاده کردن از یکی از این توابع و استفاده نکردن از اون یکی، هیچ معنایی نداره و تأثیری هم توی برنامهتون نمیذاره.