By the tip of this tutorial, it is possible for you to to attract totally different shapes of various colours. Check out the ultimate working demo beneath. Be at liberty to fork and play around with it!
HTML construction
The HTML Construction will encompass the next parts:
- A
<choose>
factor that can have 4 drop-down choices, specifically; freehand, circle, rectangle and eraser - An
<enter kind="coloration">
which is able to outline a coloration picker. - A
<canvas>
factor which will likely be draw with JavaScript.
Right here is the HTML construction (as ever, to simplify the method) utilizing Bootstrap.
1 |
<div class="container d-flex justify-content-center align-items-center mt-5"> |
2 |
<div class="row"> |
3 |
<h1>Drawing Device</h1> |
4 |
</div>
|
5 |
</div>
|
6 |
<div class="container d-flex justify-content-center align-items-center"> |
7 |
<div class="row mt-5"> |
8 |
<div class="col-md-4 col-sm-6 col-8"> |
9 |
<div class="mb-3 d-flex justify-content-center align-items-center"> |
10 |
<label for="instrument">Device:</label> |
11 |
<choose id="instrument" class="form-control"> |
12 |
<choice worth="rectangle">Rectangle</choice> |
13 |
<choice worth="freehand">Freehand</choice> |
14 |
<choice worth="circle">Circle</choice> |
15 |
<choice worth="eraser">Eraser</choice> |
16 |
</choose>
|
17 |
</div>
|
18 |
<div class="mb-3 d-flex justify-content-center align-items-center"> |
19 |
<label for="drawcolor">Shade:</label> |
20 |
<enter kind="coloration" id="drawcolor" title="drawcolor" worth="#00FFFF" class="form-control" /> |
21 |
</div>
|
22 |
</div>
|
23 |
<div class="col-md-8"> |
24 |
<canvas width="600" peak="450"></canvas> |
25 |
</div>
|
26 |
</div>
|
27 |
</div>
|
For the canvas, we’re setting a customized width and peak with <canvas width="600" peak="450"></canvas>
.
The customized setting defines the dimensions and also will be certain that the drawing space is appropriately scaled and permits for exact management over the scale of the canvas.
Styling with CSS
Add the next customized types:
1 |
@import url("https://fonts.googleapis.com/css2?household=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&show=swap"); |
2 |
physique { |
3 |
background-color: rgb(247, 248, 249); |
4 |
font-family: "DM Mono", monospace; |
5 |
}
|
6 |
canvas { |
7 |
border: 1px strong rgb(33, 32, 32); |
8 |
border-radius: 10px; |
9 |
background-color: #fff; |
10 |
cursor: crosshair; |
11 |
}
|
12 |
h1{ |
13 |
font-weight:600; |
14 |
}
|
The customized types function a customized Google font, a border, a border radius to the canvas factor, and a white background coloration.
JavaScript performance
Begin by getting the canvas factor
1 |
const canvas = doc.querySelector("canvas"); |
Subsequent, create a 2D context object which is able to enable us to attract within the canvas. The 2D context object accommodates strategies for drawing on the canvas.
1 |
ctx = canvas.getContext("2nd", { willReadFrequently: true }); |
Outline some preliminary variables:
1 |
let isDrawing = false; |
2 |
let startX = 0; |
3 |
let startY = 0; |
4 |
let initialImageData; |
-
isDrawing
: this variable will maintain monitor of when the canvas is being drawn on. -
startX
is the preliminary level on the X axis on the canvas the place any drawing will begin. -
startY
is the preliminary level on the y axis on the canvas the place any drawing will begin. -
initialImageData
is used to make a copy of how the canvas regarded earlier than any drawing begins. It is also helpful for stopping trails when new shapes are drawn.
Add occasion listeners to get the chosen coloration and gear:
1 |
selectTool = doc.getElementById("instrument"); |
2 |
|
3 |
let currentTool = selectTool.worth; |
4 |
|
5 |
selectedColor = doc.getElementById("drawcolor"); |
6 |
let coloration = selectedColor.worth; |
7 |
|
8 |
selectedColor.addEventListener("enter", () => { |
9 |
coloration = selectedColor.worth; |
10 |
});
|
11 |
|
12 |
selectTool.addEventListener("change", () => { |
13 |
currentTool = selectTool.worth; |
14 |
});
|
Subsequent, add an occasion listener to the canvas on the mousedown
occasion. The mousedown
occasion will invoke the startDrawing()
operate.
1 |
canvas.addEventListener("mousedown", startDrawing); |
Create the referred to as startDrawing()
operate which is able to appear to be this:
1 |
operate startDrawing(e) { |
2 |
ctx.lineWidth = 5; |
3 |
startX = e.offsetX; |
4 |
startY = e.offsetY; |
5 |
isDrawing = true; |
6 |
ctx.beginPath(); |
7 |
|
8 |
ctx.fillStyle = coloration; |
9 |
ctx.strokeStyle = coloration; |
10 |
initialImageData = ctx.getImageData(0, 0, canvas.width, canvas.peak); |
11 |
}
|
Within the code above, on the mousedown
occasion , we are going to use the linewidth()
technique supplied by the 2D context object to set a customized measurement for the drawing line width in pixels.
-
isDrawing = true;
units theIsDrawing
worth to true to suggest that the drawing has began. -
startX = e.offsetX
; will set the worth of startX to the x-coordinate of the mouse pointer. -
startY = e.offsetY;
will set the worth ofstartY
to the y-cordinate of the mouse pointer. -
ctx.beginPath(); beginPath ()
is a 2D context technique which begins a brand new path. On this case, a brand new path will likely be began on the intersection ofstartX
andstartY
. -
ctx.fillStyle = coloration;
will set the colour used to fill the drawing -
ctx.strokeStyle = coloration;
units the chosen coloration because the stroke coloration.
Subsequent, add an occasion listener to the canvas on the mousemove
occasion. The mousemove
occasion will invoke the Drawing()
operate.
1 |
canvas.addEventListener("mousemove", drawing); |
When the consumer strikes the mouse, create a operate referred to as drawing which is able to first examine for the isDrawing
situation, if its not true, the operate will exit.
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
}
|
If the isDrawing
situation is occurring, we are going to use situations to examine the present chosen instrument and replace appropriately. We’ll create case change statements for every of the next instruments:
- freehand
- circle
- triangle
- eraser
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
|
4 |
|
5 |
change (currentTool) { |
6 |
case "freehand": |
7 |
// use freehand instrument
|
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 |
}
|
For the freehand instrument, replace the operate as proven beneath:
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
change (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 |
// remainder of the code
|
12 |
}}
|
When the freehand instrument is chosen, we are going to do the next:
-
ctx.moveTo(startX, startY);
will transfer the drawing cursor to the place to begin -
ctx.lineTo(e.offsetX, e.offsetY);
will add a line from the place to begin to the present mouse place -
ctx.stroke();
will draw the road path with the chosen coloration. -
startX = e.offsetX; and startY = e.offsetY;
will reset the beginning factors.
When the rectangle is chosen, replace the operate as follows:
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
change (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 peak = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, peak); |
18 |
ctx.beginPath(); |
19 |
break; |
20 |
}}
|
-
const width = e.offsetX - startX;
The width is obtained by the distinction between the beginning place, represented bystartX
and the currrent x-cordinate of the mouse pointer. -
const peak = e.offsetY - startY;
To get the peak, we get the distinction between the beginning place, represented bystartY
and the currrent y-cordinate of the mouse pointer. -
ctx.fillRect(startX, startY, width, peak);
thefillRect()
technique will draw a crammed rectangle. This technique takes in parameters within the order supplied.
To attract a circle, we first have to get the circle’s radius, we are going to then use the .arc()
technique to attract a curve to the desired path. The .arc()
technique has the next syntax.
1 |
context.arc(x, y, r, startAngle, endAngle, counterclockwise) |
the place
x
andy
are the x and y-coordinate of the middle of the circle-
r
is the radius of the circle, which is calculated by the gap from the middle to any level within the circumference of the circle. To get the radius of the circle, we are going to use the Pythagoras theorem -
startAngle
is the angle at which the trail begins, measured in radians. In the context of a circle, that is sometimes set to 0, indicating the place to begin of the trail -
endAngle
is the angle at which the trail ends in radians. It’s obtained by2*PI
(corresponds to 360 levels)
Let’s get the radius utilizing the Pythagoras theorem.
1 |
const radius = Math.sqrt( |
2 |
(e.offsetX - startX) ** 2 + (e.offsetY - startY) ** 2 |
3 |
);
|
Now if we substitute our values within the .arc()
technique, the code for drawing a circle will appear to be this:
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
change (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 peak = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, peak); |
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 |
}
|
Lastly, for the eraser instrument, replace the operate as follows:
1 |
operate drawing(e) { |
2 |
if (!isDrawing) return; |
3 |
ctx.putImageData(initialImageData, 0, 0); |
4 |
|
5 |
change (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 peak = e.offsetY - startY; |
17 |
ctx.fillRect(startX, startY, width, peak); |
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 |
}
|
The erase performance is just like the freehand instrument, besides that it makes use of the colour white to cowl any earlier colours.
The final performance is the stopDrawing()
operate which occurs on mouseup
occasion which is able to appear to be this;
1 |
canvas.addEventListener("mouseup", stopDrawing); |
2 |
operate stopDrawing(e) { |
3 |
isDrawing = false; |
4 |
ctx.closePath(); |
5 |
}
|
On the mouseup
occasion, drawing ought to cease and the present path ought to be closed. That is to make sure that no additional drawing operations happen till a brand new mousedown
occasion happens.
The ctx.closePath()
technique is used to shut the present path, guaranteeing that the form being drawn is finalized.
Closing demo
Let’s remind ourselves what now we have constructed! Right here is the demo:
Conclusion
This tutorial has lined easy methods to create a drawing app with Vanilla JavaScript. You’ll be able to additional improve this app by including options comparable to the flexibility to save lots of drawings, customized brush sizes, totally different shapes and instruments, and so forth.