Dev Bytes – This tutorial demonstrates how to create a simple drawing app using Vue.js and HTML5 Canvas.

The final result is a basic but functional drawing app that showcases the power of combining Vue and HTML5 Canvas.

In this tutorial, we will be building a simple drawing app with Vue 3 and Html5 Canvas.

HTML’s <canvas> element is an excellent tool for rendering graphics in web applications.

Vue is a fantastic JavaScript framework that provides a powerful yet intuitive platform for creating dynamic user interfaces.

Video Tutorial of this guide:

Through the combination of these two technologies, we’ll create a drawing app that demonstrates the full potential of both HTML5 and Vue.js.

So let’s dive in and start building!

Building the file structure

Let’s start by creating a file structure for the application. We will basically need just 3 files: index.html ,main.css and app.js file(s). To create these files open up the terminal and type the following code:

mkdir drawing-app && cd drawing-app
touch index.html app.js main.css

After creating these files, let’s set up our index.html file and bring Vue into the project. Let’s define a simple boilerplate and then link the CSS stylesheet and the javascript to the index.html file and also initialize Vue by adding the Vue development CDN. Open up the terminal and add the following code to your index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drawing App</title>
    <link rel="stylesheet" href="main.css">
</head>
<body>
    <div id="app">
  <h1>{{ message }}</h1>
 <canvas id="canvas"></canvas>
</div>
   
       <script src="https://unpkg.com/vue@next"></script>
    <script src="app.js"></script>
</body>
</html>

And then to set up Vue 3, let’s also add the following to the app.js file. We start by Importing Vue 3 global build utilities

const { createApp, ref, onMounted } = Vue;
const app = createApp({
setup() {
const message = ref("Drawing App");
const painting = ref(false);
const canvas = ref(null);
const ctx = ref(null);
const colors = ref(["#000000", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF"]);

The code above destructures createApp, ref, and onMounted from the global Vue object. These utilities are used to create the app, handle reactivity, and manage the component lifecycle, respectively.

createApp is used to set up the Vue application and make sure it’s ready to show the web page on the screen. It’s like the foundation of the web page.

In Vue 3, the setup function is used to manage the component’s state and logic using the Composition API.

ref is used to help keep track of different parts of the web page that might change as a user interacts with it. For example, the title of the web page or whether a user is painting on a canvas.

Run The Live Server

We can run our application by using live-server. If you don’t have live-server installed on your local machine, then install it by running npm i live-server -g 

The -g flag will install the package globally in our local machine. After installing it, you can now move into your development project using the terminal by using the cd command followed by the location of your application.

To run our application using live-server open up the terminal and run the following commands:

cd drawing-app && live-server --port=4040

This will open up our application on a live server running on port 4040.

Let’s continue building our application.

Let’s continue by defining the changeColor function

const changeColor = (color) => {
  ctx.value.strokeStyle = color;
};

This function changes the stroke color when a user selects a different color from the color picker. It takes a color parameter and sets the stroke style of the canvas context.

Next, Define the clearCanvas function:

const clearCanvas = () => {
  ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
};

This function clears the canvas when the user clicks the “Clear Canvas” button. It uses the clearRect method to erase the content of the canvas.

Now, we will define Define the startPainting, finishedPainting, and draw functions:

const startPainting = (e) => {
  painting.value = true;
  draw(e);
};

const finishedPainting = () => {
  painting.value = false;
  ctx.value.beginPath();
};

const draw = (e) => {
  if (!painting.value) return;

  ctx.value.lineWidth = 10;
  ctx.value.lineCap = "round";

  ctx.value.lineTo(e.clientX - canvas.value.offsetLeft, e.clientY - canvas.value.offsetTop);
  ctx.value.stroke();

  ctx.value.beginPath();
  ctx.value.moveTo(e.clientX - canvas.value.offsetLeft, e.clientY - canvas.value.offsetTop);
};

These functions are responsible for handling the drawing functionality of the app. startPainting is triggered when the user presses the mouse button, setting painting to true. finishedPainting is called when the user releases the mouse button, setting painting to false. The draw function is called during mouse movement, and it handles the actual drawing on the canvas.

Now we will use onMounted to run code after the component is mounted:

onMounted(() => {
  canvas.value = document.getElementById("canvas");
  ctx.value = canvas.value.getContext("2d");

  // Set default stroke color
  ctx.value.strokeStyle = colors.value[0];

  // Resize canvas
  canvas.value.height = window.innerHeight * 0.6;
  canvas.value.width = window.innerWidth * 0.8;
});

The onMounted the function takes a callback that will be executed after the component is mounted. In our case, we initialize the canvas and ctx references, set the default stroke color, and resize the canvas.

Now, Return the exposed variables and methods:

return {
  message,
  painting,
  canvas,
  ctx,
  colors,
  changeColor,
  clearCanvas,
  startPainting,
  finishedPainting,
  draw,
  };
    },
  }).mount("#app");

In the setup function, we return an object containing the reactive variables and methods that will be exposed to the template and mounts the Vue app to the DOM element with the ID app.

Full code Our code in app.js file:

const { createApp, ref, onMounted } = Vue;

const app = createApp({
  setup() {
    const message = ref("Drawing App");
    const painting = ref(false);
    const canvas = ref(null);
    const ctx = ref(null);
    const colors = ref(["#000000", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF"]);

    const changeColor = (color) => {
        ctx.value.strokeStyle = color;
      };
  
      const clearCanvas = () => {
        ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height);
      };
  
      const startPainting = (e) => {
        painting.value = true;
        draw(e);
      };
  
      const finishedPainting = () => {
        painting.value = false;
        ctx.value.beginPath();
      };
  
      const draw = (e) => {
        if (!painting.value) return;
  
        ctx.value.lineWidth = 10;
        ctx.value.lineCap = "round";
  
        ctx.value.lineTo(e.clientX - canvas.value.offsetLeft, e.clientY - canvas.value.offsetTop);
        ctx.value.stroke();
  
        ctx.value.beginPath();
        ctx.value.moveTo(e.clientX - canvas.value.offsetLeft, e.clientY - canvas.value.offsetTop);
      };
  
      onMounted(() => {
        canvas.value = document.getElementById("canvas");
        ctx.value = canvas.value.getContext("2d");
  
        // Set default stroke color
        ctx.value.strokeStyle = colors.value[0];
  
        // Resize canvas
        canvas.value.height = window.innerHeight * 0.6;
        canvas.value.width = window.innerWidth * 0.8;
      });
  
      return {
        message,
        painting,
        canvas,
        ctx,
        colors,
        changeColor,
        clearCanvas,
        startPainting,
        finishedPainting,
        draw,
      };
    },
  }).mount("#app");
  

Now we want a situation when the mouse is down we set the value of the painting to true and when the mouse is up we then set the value of the painting to false. So we add event listeners to the canvas tag in the index.html file:

<canvas @mousedown="startPainting" @mouseup="finishedPainting" @mousemove="draw" id="canvas"></canvas>

The <canvas> element is used for drawing graphics. It has event listeners for mousedown, mouseup, and mousemove events, which are connected to the startPainting, finishedPainting, and draw functions in app.js.

Next, Setup the Clear canvas button:

<a class="clear-button" @click.prevent="clearCanvas">Clear Canvas</a>

The <a> element acts as a button to clear the canvas. The @click.prevent event listener calls the clearCanvas function from app.js and uses the prevent modifier to prevent the default behavior of the anchor tag.

Full code of Our code index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drawing App</title>
    <link rel="stylesheet" href="main.css">
</head>
<body>
    <div id="app">
        <h2>{{ message }}</h2>
        <div class="color-picker">
            <div v-for="color in colors" class="color-box" :style="{ backgroundColor: color }" @click="changeColor(color)"></div>
        </div>
        <canvas @mousedown="startPainting" @mouseup="finishedPainting" @mousemove="draw" id="canvas"></canvas>
        <a class="clear-button" @click.prevent="clearCanvas">Clear Canvas</a>
    </div>
   
    <script src="https://unpkg.com/vue@next"></script>
    <script src="app.js"></script>
</body>
</html>

In our main.css file lets add borders to the canvas and some modern styling to make our app look elegant

* {
    box-sizing: border-box;
  }
  
  body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
  }
  
  #app {
    width: 90%;
    max-width: 1200px;
    background-color: #fff;
    padding: 2rem;
    border-radius: 1rem;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  }
  
  h2 {
    text-align: center;
    margin-bottom: 1rem;
  }
  
  canvas {
    display: block;
    background-color: #ffffff;
    border: 2px solid #333;
    border-radius: 5px;
    cursor: crosshair;
  }
  
  .color-box {
    display: inline-block;
    width: 20px;
    height: 20px;
    margin: 0 5px;
    cursor: pointer;
    border-radius: 50%;
  }
  
  .color-picker {
    display: flex;
    justify-content: center;
    margin-bottom: 1rem;
  }
  
  .clear-button {
    display: block;
    margin: 1rem auto;
    padding: 0.5rem 1rem;
    background-color: #333;
    color: #fff;
    text-align: center;
    border-radius: 5px;
    cursor: pointer;
    text-decoration: none;
    font-weight: bold;
  }
  
  .clear-button:hover {
    background-color: #444;
  }
  

And that’s it. We just used the canvas API to build a simple painting app using Vue 3.

Drawing app with Vue 3
Drawing App built using Vue 3 and Html5 canvas API

Conclusion

In this tutorial, we showed how easy it is to create a drawing app using the Canvas API and Vue. We also learned how to work with mouse event listeners in Vue.

This example can be a starting point for creating more advanced apps or adding new features like changing pen colors and canvas backgrounds. The source code is available here.