DEV Community

Cover image for Lets create a Drawing APP with JS
Shuvo
Shuvo

Posted on

Lets create a Drawing APP with JS

In this article I will show you how you can create a Drawing/Pain APP using JavaScript and HTML5 canvas.

Features:

  1. Draw on Canvas
  2. Multiple colors
  3. Clear canvas
  4. Save drawing as Image JavaScript Paint app demo

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>
Enter fullscreen mode Exit fullscreen mode

Now let's create the style.css with basic resets

*{
    margin: 0;
    padding: 0;
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Now if we open it in our browser we should see a red rectangle.
Basing shape on canvas

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)
})
Enter fullscreen mode Exit fullscreen mode

Get mouse position in JS
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
})
Enter fullscreen mode Exit fullscreen mode

Drawing line on mouse move javascript canvas
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
})
Enter fullscreen mode Exit fullscreen mode

Basic JavaScript paint app

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Okay the page should look something like this.
Drawing app with buttons
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
})
Enter fullscreen mode Exit fullscreen mode

Multi color drawing app
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
})
Enter fullscreen mode Exit fullscreen mode

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
})
Enter fullscreen mode Exit fullscreen mode

Paint app final demo

And there, We have done it. You can get the complete source code here.
Make sure you checkout my other articles and YouTube channel

Was it helpful? Support me on Patreon

Patreon Logo

Discussion (30)

Collapse
redeakaa profile image
Okoro Redemption

Beautiful

Collapse
0shuvo0 profile image
Shuvo Author

Many many thanks ❤

Collapse
prabhukadode profile image
Prabhu

Awesome

Collapse
0shuvo0 profile image
Shuvo Author

Glad you liked it

Collapse
drsimplegraffiti profile image
Abayomi Ogunnusi

This is awesome

Collapse
0shuvo0 profile image
Shuvo Author

Glad you liked it

Collapse
waiylkarim profile image
WAIYL KARIM

Brilliant

Collapse
0shuvo0 profile image
Shuvo Author

Many thanks

Collapse
chiragshivam profile image
Chivi

Wow, that's pretty cool, will definitely try this!

Collapse
0shuvo0 profile image
Shuvo Author

Sure, glad you liked it

Collapse
zyabxwcd profile image
Akash

Amazing. Well explained, clear and readable code.

Collapse
0shuvo0 profile image
Shuvo Author

Thanks a lot 💓
Glad you liked it

Collapse
zdoten profile image
z.En

It's amazing!

Collapse
0shuvo0 profile image
Shuvo Author

Many thanks

Collapse
cmgustin profile image
Chris Gustin

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.

Collapse
0shuvo0 profile image
Shuvo Author

Many many thanks
I'm really glad you liked it

Collapse
enriquesource profile image
EnriqueSource

Really awesome. Thanks

Collapse
bbaublys profile image
Борис Баублис

Excellent thank you. Any ideas how to save the coordinates of the created objects now? For example, in a Google sheet.

Collapse
0shuvo0 profile image
Shuvo Author

On mouse move along with drawing a line you can save coords in a array

Collapse
minhazhalim profile image
Minhaz Halim (Zim)

Awesome

Collapse
0shuvo0 profile image
Shuvo Author

Thanks 💓

Collapse
salehmubashar profile image
Saleh Mubashar

very cool!
Is there any other possible approach for drawing the line.
instead of using prev and current can we use any other way
just curious
thanks

Collapse
0shuvo0 profile image
Shuvo Author

Well you need to know two point between which the line should be drawn

Collapse
theb055 profile image
THEB055

Useful 🖤

Collapse
0shuvo0 profile image
Shuvo Author

Gald it helped

Collapse
facundoruiz profile image
facundo

Muy bueno, aplica le para una app de firmas.
Me trajo recuerdos de cuando tuvimos que hacer en C++ una app que pueda dibujar un punto , cuadrado , circulo aplicando POO.

Collapse
0shuvo0 profile image
Shuvo Author

Sorry I didn't understood