Lets create a Drawing APP with JS

Shuvo - Nov 10 '21 - - Dev Community

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .