در پایان این آموزش، شما قادر خواهید بود اشکال مختلف با رنگ های مختلف را ترسیم کنید. به نسخه ی نمایشی کاری نهایی در زیر نگاهی بیندازید. به راحتی چنگال بزنید و با آن بازی کنید!
ساختار HTML
ساختار HTML از اجزای زیر تشکیل خواهد شد:
در اینجا ساختار HTML (مانند همیشه، برای ساده کردن فرآیند) با استفاده از Bootstrap است.
1 |
|
2 |
|
3 |
Drawing Tool |
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
type="color" id="drawcolor" name="drawcolor" value="#00FFFF" class="form-control" />
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
برای بوم، عرض و ارتفاع سفارشی را با آن تنظیم می کنیم .
تنظیم سفارشی اندازه را تعیین می کند و همچنین اطمینان حاصل می کند که منطقه طراحی به طور مناسب مقیاس بندی شده است و امکان کنترل دقیق بر روی ابعاد بوم را فراهم می کند.
استایل دهی با CSS
سبک های سفارشی زیر را اضافه کنید:
1 |
@import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); |
2 |
body { |
3 |
background-color: rgb(247, 248, 249); |
4 |
font-family: "DM Mono", monospace; |
5 |
}
|
6 |
canvas { |
7 |
border: 1px solid rgb(33, 32, 32); |
8 |
border-radius: 10px; |
9 |
background-color: #fff; |
10 |
cursor: crosshair; |
11 |
}
|
12 |
h1{ |
13 |
font-weight:600; |
14 |
}
|
سبک های سفارشی دارای فونت سفارشی گوگل، حاشیه، شعاع حاشیه به عنصر بوم و رنگ پس زمینه سفید هستند.
قابلیت جاوا اسکریپت
با گرفتن عنصر بوم شروع کنید
1 |
const canvas = document.querySelector("canvas"); |
در مرحله بعد، یک شی زمینه دوبعدی ایجاد کنید که به ما امکان می دهد در بوم نقاشی بکشیم. شی زمینه دوبعدی شامل روش هایی برای طراحی روی بوم است.
1 |
ctx = canvas.getContext("2d", { willReadFrequently: true }); |
چند متغیر اولیه را تعریف کنید:
1 |
let isDrawing = false; |
2 |
let startX = 0; |
3 |
let startY = 0; |
4 |
let initialImageData; |
-
isDrawing
: این متغیر زمان طراحی بوم را پیگیری می کند. -
startX
نقطه اولیه در محور X روی بوم است که هر طراحی از آنجا شروع می شود. -
startY
نقطه اولیه در محور y روی بوم است که هر طراحی از آنجا شروع می شود. -
initialImageData
برای نگهداری یک کپی از ظاهر بوم قبل از شروع هر طراحی استفاده می شود. همچنین برای جلوگیری از ردیابی هنگام ترسیم اشکال جدید مفید است.
برای دریافت رنگ و ابزار انتخابی شنوندگان رویداد را اضافه کنید:
1 |
selectTool = document.getElementById("tool"); |
2 |
|
3 |
let currentTool = selectTool.value; |
4 |
|
5 |
selectedColor = document.getElementById("drawcolor"); |
6 |
let color = selectedColor.value; |
7 |
|
8 |
selectedColor.addEventListener("input", () => { |
9 |
color = selectedColor.value; |
10 |
});
|
11 |
|
12 |
selectTool.addEventListener("change", () => { |
13 |
currentTool = selectTool.value; |
14 |
});
|
سپس، شنونده رویداد را به بوم روی بوم اضافه کنید mousedown
رویداد را mousedown
رویداد فراخوانی خواهد شد startDrawing()
تابع
1 |
canvas.addEventListener("mousedown", startDrawing); |
فراخوانی را ایجاد کنید startDrawing()
تابعی که به شکل زیر خواهد بود:
1 |
function startDrawing(e) { |
2 |
ctx.lineWidth = 5; |
3 |
startX = e.offsetX; |
4 |
startY = e.offsetY; |
5 |
isDrawing = true; |
6 |
ctx.beginPath(); |
7 |
|
8 |
ctx.fillStyle = color; |
9 |
ctx.strokeStyle = color; |
10 |
initialImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); |
11 |
}
|
در کد بالا، در mousedown
رویداد، ما از linewidth()
روش ارائه شده توسط شی زمینه دوبعدی برای تنظیم اندازه سفارشی برای عرض خط ترسیم بر حسب پیکسل.
-
isDrawing = true;
را تنظیم می کندIsDrawing
مقدار به true به این معنی است که طراحی شروع شده است. -
startX = e.offsetX
; مقدار startX را روی مختصات x نشانگر ماوس تنظیم می کند. -
startY = e.offsetY;
مقدار را تعیین خواهد کردstartY
به مختصات y اشاره گر ماوس. -
ctx.beginPath(); beginPath ()
یک روش زمینه دو بعدی است که مسیر جدیدی را آغاز می کند. در این صورت یک مسیر جدید در تقاطع شروع می شودstartX
وstartY
. -
ctx.fillStyle = color;
رنگ مورد استفاده برای پر کردن نقاشی را تنظیم می کند -
ctx.strokeStyle = color;
رنگ انتخاب شده را به عنوان رنگ ضربه ای تنظیم می کند.
سپس، شنونده رویداد را به بوم روی بوم اضافه کنید mousemove
رویداد را mousemove
رویداد فراخوانی خواهد شد Drawing()
تابع
1 |
canvas.addEventListener("mousemove", drawing); |
هنگامی که کاربر ماوس را حرکت می دهد، تابعی به نام ترسیم ایجاد کنید که ابتدا آن را بررسی می کند isDrawing
شرط، اگر درست نباشد، تابع خارج خواهد شد.
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
}
|
اگر isDrawing
شرایط در حال وقوع است، ما از شرایط برای بررسی ابزار انتخابی فعلی و به روز رسانی مناسب استفاده خواهیم کرد. برای هر یک از ابزارهای زیر دستورات سوئیچ case ایجاد می کنیم:
- دست آزاد
- دایره
- مثلث
- پاک کن
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
|
4 |
|
5 |
switch (currentTool) { |
6 |
case "freehand": |
7 |
// use freehand tool
|
8 |
break; |
9 |
|
10 |
case "rectangle": |
11 |
// draw rectangle
|
12 |
break; |
13 |
|
14 |
case "circle": |
15 |
// draw circle
|
16 |
break; |
17 |
|
18 |
case "eraser": |
19 |
//erase
|
20 |
break; |
21 |
|
22 |
default: |
23 |
break; |
24 |
}
|
25 |
}
|
برای ابزار دست آزاد، تابع را مطابق شکل زیر به روز کنید:
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
switch (currentTool) { |
4 |
case "freehand": |
5 |
ctx.moveTo(startX, startY); |
6 |
ctx.lineTo(e.offsetX, e.offsetY); |
7 |
ctx.stroke(); |
8 |
startX = e.offsetX; |
9 |
startY = e.offsetY; |
10 |
break; |
11 |
// rest of the code
|
12 |
}}
|
وقتی ابزار freehand انتخاب شد، موارد زیر را انجام خواهیم داد:
-
ctx.moveTo(startX, startY);
مکان نما ترسیم را به نقطه شروع حرکت می دهد -
ctx.lineTo(e.offsetX, e.offsetY);
یک خط از نقطه شروع به موقعیت فعلی ماوس اضافه می کند -
ctx.stroke();
مسیر خط را با رنگ انتخاب شده ترسیم می کند. -
startX = e.offsetX; and startY = e.offsetY;
نقاط شروع را بازنشانی خواهد کرد.
هنگامی که مستطیل انتخاب شد، تابع را به صورت زیر به روز کنید:
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
switch (currentTool) { |
6 |
case "freehand": |
7 |
ctx.moveTo(startX, startY); |
8 |
ctx.lineTo(e.offsetX, e.offsetY); |
9 |
ctx.stroke(); |
10 |
startX = e.offsetX; |
11 |
startY = e.offsetY; |
12 |
break; |
13 |
|
14 |
case "rectangle": |
15 |
const width = e.offsetX - startX; |
16 |
const height = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, height); |
18 |
ctx.beginPath(); |
19 |
break; |
20 |
}}
|
-
const width = e.offsetX - startX;
عرض با تفاوت بین موقعیت شروع به دست می آید که با نشان داده شده استstartX
و مختصات x فعلی نشانگر ماوس. -
const height = e.offsetY - startY;
برای بدست آوردن ارتفاع، تفاوت بین موقعیت شروع را بدست می آوریم که با نشان داده می شودstartY
و مختصات y فعلی نشانگر ماوس. -
ctx.fillRect(startX, startY, width, height);
راfillRect()
روش یک مستطیل پر را رسم می کند. این روش پارامترها را به ترتیب ارائه شده می گیرد.
برای رسم دایره ابتدا باید شعاع دایره را بدست آوریم سپس از آن استفاده می کنیم .arc()
روش رسم منحنی به مسیر مشخص شده را .arc()
متد دستور زیر را دارد.
1 |
context.arc(x, y, r, startAngle, endAngle, counterclockwise) |
کجا
x
وy
مختصات x و y مرکز دایره هستند-
r
است شعاع دایره، که محاسبه می شود با فاصله از مرکز تا هر نقطه از محیط دایره. برای بدست آوردن شعاع دایره از فیثاغورث استفاده می کنیم قضیه -
startAngle
زاویه ای است که مسیر در آن شروع می شود که بر حسب رادیان اندازه گیری می شود. در زمینه از یک دایره، معمولاً روی 0 تنظیم می شود که نقطه شروع مسیر را نشان می دهد -
endAngle
است زاویه ای که مسیر در آن به رادیان ختم می شود. به دست می آید2*PI
(مطابق با 360 درجه)
بیایید شعاع را با استفاده از فیثاغورث قضیه
1 |
const radius = Math.sqrt( |
2 |
(e.offsetX - startX) ** 2 + (e.offsetY - startY) ** 2 |
3 |
);
|
حال اگر مقادیر خود را در آن جایگزین کنیم .arc()
در روش، کد رسم دایره به شکل زیر خواهد بود:
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
switch (currentTool) { |
6 |
case "freehand": |
7 |
ctx.moveTo(startX, startY); |
8 |
ctx.lineTo(e.offsetX, e.offsetY); |
9 |
ctx.stroke(); |
10 |
startX = e.offsetX; |
11 |
startY = e.offsetY; |
12 |
break; |
13 |
|
14 |
case "rectangle": |
15 |
const width = e.offsetX - startX; |
16 |
const height = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, height); |
18 |
ctx.beginPath(); |
19 |
break; |
20 |
|
21 |
case "circle": |
22 |
const radius = Math.sqrt( |
23 |
(e.offsetX - startX) ** 2 + (e.offsetY - startY) ** 2 |
24 |
);
|
25 |
ctx.beginPath(); |
26 |
ctx.arc(startX, startY, radius, 0, 2 * Math.PI); |
27 |
ctx.fill(); |
28 |
ctx.stroke(); |
29 |
break; |
30 |
|
31 |
|
32 |
}
|
33 |
}
|
در نهایت، برای ابزار پاک کن، تابع را به صورت زیر به روز کنید:
1 |
function drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
switch (currentTool) { |
6 |
case "freehand": |
7 |
ctx.moveTo(startX, startY); |
8 |
ctx.lineTo(e.offsetX, e.offsetY); |
9 |
ctx.stroke(); |
10 |
startX = e.offsetX; |
11 |
startY = e.offsetY; |
12 |
break; |
13 |
|
14 |
case "rectangle": |
15 |
const width = e.offsetX - startX; |
16 |
const height = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, height); |
18 |
ctx.beginPath(); |
19 |
break; |
20 |
|
21 |
case "circle": |
22 |
const radius = Math.sqrt( |
23 |
(e.offsetX - startX) ** 2 + (e.offsetY - startY) ** 2 |
24 |
);
|
25 |
ctx.beginPath(); |
26 |
ctx.arc(startX, startY, radius, 0, 2 * Math.PI); |
27 |
ctx.fill(); |
28 |
ctx.stroke(); |
29 |
break; |
30 |
|
31 |
case "eraser": |
32 |
ctx.strokeStyle = "#fff"; |
33 |
ctx.moveTo(startX, startY); |
34 |
ctx.lineTo(e.offsetX, e.offsetY); |
35 |
ctx.stroke(); |
36 |
startX = e.offsetX; |
37 |
startY = e.offsetY; |
38 |
break; |
39 |
|
40 |
default: |
41 |
break; |
42 |
}
|
43 |
}
|
قابلیت پاک کردن شبیه ابزار دست آزاد است، با این تفاوت که از رنگ سفید برای پوشش هر رنگ قبلی استفاده می کند.
آخرین قابلیت این است stopDrawing()
عملکردی که روی آن اتفاق می افتد mouseup
رویدادی که شبیه این خواهد بود؛
1 |
canvas.addEventListener("mouseup", stopDrawing); |
2 |
function stopDrawing(e) { |
3 |
isDrawing = false; |
4 |
ctx.closePath(); |
5 |
}
|
در mouseup
رویداد، ترسیم باید متوقف شود و مسیر فعلی باید بسته شود. این برای اطمینان از این است که هیچ عملیات ترسیم دیگری تا زمانی که یک نقشه جدید رخ نمی دهد، انجام شود mousedown
رویداد رخ می دهد
را ctx.closePath()
این روش برای بستن مسیر فعلی استفاده می شود و از نهایی شدن شکل ترسیم شده اطمینان حاصل می شود.
نسخه ی نمایشی نهایی
بیایید به خودمان یادآوری کنیم که چه ساخته ایم! در اینجا نسخه ی نمایشی است:
نتیجه گیری
این آموزش نحوه ایجاد یک برنامه نقاشی با جاوا اسکریپت Vanilla را پوشش می دهد. میتوانید با افزودن ویژگیهایی مانند قابلیت ذخیره نقشهها، اندازههای براش سفارشی، اشکال و ابزارهای مختلف و غیره، این برنامه را بیشتر تقویت کنید.
منبع: https://webdesign.tutsplus.com/how-to-create-a-canvas-drawing-tool-with-vanilla-javascript–cms-108856t