In this article I will show you how you can create a Drawing/Pain APP using JavaScript and HTML5 canvas.
Features:
- Draw on Canvas
- Multiple colors
- Clear canvas
- Save drawing as Image
First lets create a index.html file with a canvas element.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>JavaScript Drawing APP</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>
Now let's create the style.css with basic resets
*{
margin: 0;
padding: 0;
}
And then finally we will create our main.js where we will target our canvas and set it's size to the size of our screen.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
// ctx is the context of our canvas
// we use ctx to draw on the canvas
const ctx = canvas.getContext("2d")
// lets create a rectangle for testing purposes
ctx.fillStyle = "red"
ctx.fillRect(100, 100, 100, 100)
Now if we open it in our browser we should see a red rectangle.
Okay lets delete the rectangle and whenever the use moves his mouse we want to get the mouse position. We can use mousemove
event for that.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
window.addEventListener("mousemove", (e) => {
console.log("Mouse X: " + e.clientX)
console.log("Mouse Y: " + e.clientY)
})
Great!!! Now we also need to keep track of the previous mouse position and draw a line from the previous mouse position to current mouse position.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
// previous mouse positions
// They will be null initially
let prevX = null
let prevY = null
// How thick the lines should be
ctx.lineWidth = 5
window.addEventListener("mousemove", (e) => {
// initially previous mouse positions are null
// so we can't draw a line
if(prevX == null || prevY == null){
// Set the previous mouse positions to the current mouse positions
prevX = e.clientX
prevY = e.clientY
return
}
// Current mouse position
let currentX = e.clientX
let currentY = e.clientY
// Drawing a line from the previous mouse position to the current mouse position
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
// Update previous mouse position
prevX = currentX
prevY = currentY
})
Now if you move your mouse you will see a line will be drawn. But we don't want the line to be drawn uncontrollably. So we will declare a variable let draw = false
. And we will only draw in draw
is true
.
So we can listen to the mousedown
and mouseup
event. And set draw
to true
when user presses the mouse and false
when releases the mouse.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
let prevX = null
let prevY = null
ctx.lineWidth = 5
let draw = false
// Set draw to true when mouse is pressed
window.addEventListener("mousedown", (e) => draw = true)
// Set draw to false when mouse is released
window.addEventListener("mouseup", (e) => draw = false)
window.addEventListener("mousemove", (e) => {
// if draw is false then we won't draw
if(prevX == null || prevY == null || !draw){
prevX = e.clientX
prevY = e.clientY
return
}
let currentX = e.clientX
let currentY = e.clientY
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
prevX = currentX
prevY = currentY
})
Awesome!!! Now lets add some button in our HTML for changing colors, clearing canvas and saving the drawing.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<canvas id="canvas"></canvas>
<div class="nav">
<!-- We will be accessing the data-clr in JavaScript -->
<div class="clr" data-clr="#000"></div>
<div class="clr" data-clr="#EF626C"></div>
<div class="clr" data-clr="#fdec03"></div>
<div class="clr" data-clr="#24d102"></div>
<div class="clr" data-clr="#fff"></div>
<button class="clear">clear</button>
<button class="save">save</button>
</div>
<script src="main.js"></script>
</body>
</html>
And we also need to style them in our css.
*{
margin: 0;
padding: 0;
}
.nav{
width: 310px;
height: 50px;
position: fixed;
top: 0;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
justify-content: space-around;
opacity: .3;
transition: opacity .5s;
}
.nav:hover{
opacity: 1;
}
.clr{
height: 30px;
width: 30px;
background-color: blue;
border-radius: 50%;
border: 3px solid rgb(214, 214, 214);
transition: transform .5s;
}
.clr:hover{
transform: scale(1.2);
}
.clr:nth-child(1){
background-color: #000;
}
.clr:nth-child(2){
background-color: #EF626C;
}
.clr:nth-child(3){
background-color: #fdec03;
}
.clr:nth-child(4){
background-color: #24d102;
}
.clr:nth-child(5){
background-color: #fff;
}
button{
border: none;
outline: none;
padding: .6em 1em;
border-radius: 3px;
background-color: #03bb56;
color: #fff;
}
.save{
background-color: #0f65d4;
}
Okay the page should look something like this.
Now whenever a div with a class of clr
is clicked we add to set the color of our line to the data-clr
property of that div.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
let prevX = null
let prevY = null
ctx.lineWidth = 5
let draw = false
// Selecting all the div that has a class of clr
let clrs = document.querySelectorAll(".clr")
// Converting NodeList to Array
clrs = Array.from(clrs)
clrs.forEach(clr => {
clr.addEventListener("click", () => {
ctx.strokeStyle = clr.dataset.clr
})
})
window.addEventListener("mousedown", (e) => draw = true)
window.addEventListener("mouseup", (e) => draw = false)
window.addEventListener("mousemove", (e) => {
if(prevX == null || prevY == null || !draw){
prevX = e.clientX
prevY = e.clientY
return
}
let currentX = e.clientX
let currentY = e.clientY
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
prevX = currentX
prevY = currentY
})
Yayy!!! Now lets make the clear button work. So when we click on it it should clear our canvas.
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
let prevX = null
let prevY = null
ctx.lineWidth = 5
let draw = false
let clrs = document.querySelectorAll(".clr")
clrs = Array.from(clrs)
clrs.forEach(clr => {
clr.addEventListener("click", () => {
ctx.strokeStyle = clr.dataset.clr
})
})
let clearBtn = document.querySelector(".clear")
clearBtn.addEventListener("click", () => {
// Clearning the entire canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
})
window.addEventListener("mousedown", (e) => draw = true)
window.addEventListener("mouseup", (e) => draw = false)
window.addEventListener("mousemove", (e) => {
if(prevX == null || prevY == null || !draw){
prevX = e.clientX
prevY = e.clientY
return
}
let currentX = e.clientX
let currentY = e.clientY
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
prevX = currentX
prevY = currentY
})
Almost there!!! Now all we have top do is save our drawing when the save button is clicked.
So here is the final JavaScript codes
const canvas = document.getElementById("canvas")
canvas.height = window.innerHeight
canvas.width = window.innerWidth
const ctx = canvas.getContext("2d")
let prevX = null
let prevY = null
ctx.lineWidth = 5
let draw = false
let clrs = document.querySelectorAll(".clr")
clrs = Array.from(clrs)
clrs.forEach(clr => {
clr.addEventListener("click", () => {
ctx.strokeStyle = clr.dataset.clr
})
})
let clearBtn = document.querySelector(".clear")
clearBtn.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
})
// Saving drawing as image
let saveBtn = document.querySelector(".save")
saveBtn.addEventListener("click", () => {
let data = canvas.toDataURL("imag/png")
let a = document.createElement("a")
a.href = data
// what ever name you specify here
// the image will be saved as that name
a.download = "sketch.png"
a.click()
})
window.addEventListener("mousedown", (e) => draw = true)
window.addEventListener("mouseup", (e) => draw = false)
window.addEventListener("mousemove", (e) => {
if(prevX == null || prevY == null || !draw){
prevX = e.clientX
prevY = e.clientY
return
}
let currentX = e.clientX
let currentY = e.clientY
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
prevX = currentX
prevY = currentY
})
And there, We have done it. You can get the complete source code here.
Make sure you checkout my other articles and YouTube channel
Top comments (30)
Beautiful
Many many thanks ❤
This is awesome
Glad you liked it
Awesome
Glad you liked it
Wow, that's pretty cool, will definitely try this!
Sure, glad you liked it
Brilliant
Many thanks
It's amazing!
Many thanks
Great tutorial! This was a lot of fun to follow along with, the code was super clean, and you did a great job of clearly explaining each step.
Many many thanks
I'm really glad you liked it
Amazing. Well explained, clear and readable code.
Thanks a lot 💓
Glad you liked it
Awesome
Thanks 💓
Awesome and really clear. Thank you so much!