نحوه ایجاد یک ابزار طراحی بوم با جاوا اسکریپت وانیلی

نحوه ایجاد یک ابزار طراحی بوم با جاوا اسکریپت وانیلی

در پایان این آموزش، شما قادر خواهید بود اشکال مختلف با رنگ های مختلف را ترسیم کنید. به نسخه ی نمایشی کاری نهایی در زیر نگاهی بیندازید. به راحتی چنگال بزنید و با آن بازی کنید!

ساختار HTML

ساختار HTML از اجزای زیر تشکیل خواهد شد:

در اینجا ساختار HTML (مانند همیشه، برای ساده کردن فرآیند) با استفاده از Bootstrap است.

1

class="container d-flex justify-content-center align-items-center mt-5">

2
  

class="row">

3
    Drawing Tool
4
  
5

6

class="container d-flex justify-content-center align-items-center">

7
  

class="row mt-5">

8
    

class="col-md-4 col-sm-6 col-8">

9
      

class="mb-3 d-flex justify-content-center align-items-center">

10
        
11
        
12
          
13
          
14
          
15
          
16
        
17
      
18
      

class="mb-3 d-flex justify-content-center align-items-center">

19
        
20
         type="color" id="drawcolor" name="drawcolor" value="#00FFFF" class="form-control" />
21
      
22
    
23
    

class="col-md-8">

24
       width="600" height="450">
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