DEV Community

Rei Allen Ramos
Rei Allen Ramos

Posted on

16 2

How to use HTML Canvas with Vue

HTML 5's Canvas API is an amazing tool for creating graphics on a page. From drawing basic shapes and animation to data visualization and on-the-fly video processing, Canvas API turns your dreams to reality using JavaScript. Let's learn how to integrate it with Vue.

🦄 What we're building 🦄

4_mousedown_mouseup

1. Create a canvas

Start off by creating a canvas in your DOM. Let's add a little CSS to keep the canvas' boundaries visible.



<template>
  <div id="app">
    <h1>Drawing with mousemove event</h1>
    <canvas id="myCanvas" width="560" height="360" />
  </div>
</template>

<style>
#myCanvas {
  border: 1px solid grey;
}
</style>


Enter fullscreen mode Exit fullscreen mode

Instantiate a Vue class and hook it up to you DOM.



new Vue({
  el: '#app'
})


Enter fullscreen mode Exit fullscreen mode

1_setup_canvas

The trick to managing the canvas is by making it accessible to Vue by declaring a state and passing it the canvas' 2d context. We just need to make sure to do this after Vue and DOM have finished initialization. Enter mounted() lifecycle hook.



new Vue({
  el: '#app',
  data: {
    canvas: null,
  },
  mounted() {
    var c = document.getElementById("myCanvas");
    this.canvas = c.getContext('2d');
  }
})


Enter fullscreen mode Exit fullscreen mode

2. Reading mouse coordinates from mousemove

Using the mousemove event, we can identify the exact location of the cursor in the screen. Create an event handler called showCoordinates and pass it to the corresponding Vue directive.

The event handler will read the x- and y-coordinates from the MouseEvent interface. Use the properties offsetX and offsetY to take into consideration the (x,y) offset from the edge of the canvas. Make sure not to confuse these with clientX and clientY because they start from the top left corner of the visible screen.



<template>
  <div id="app">
    <span>{{x}}, {{y}}</span>
    <h1>Drawing with mousemove event</h1>
    <canvas id="myCanvas" width="560" height="360" @mousemove="showCoordinates"/>
  </div>
</template>


Enter fullscreen mode Exit fullscreen mode


new Vue({
  // ...
  data: {
    canvas: null,
    x: 0,
    y: 0
  },
  methods: {
    showCoordinates(e) {
      this.x = e.offsetX;
      this.y = e.offsetY;
    }
  },
  // ...
})


Enter fullscreen mode Exit fullscreen mode

2_reading_coordinates

3. Draw!

So far, so good. Knowing the exact coordinates of the cursor helps us determine where to draw on the canvas. Let's create a new function to draw a line and rename showCoordinates to draw. Inside draw, call the function to draw a line.



new Vue({
  // ...
  methods: {
    // ...
    drawLine(x1, y1, x2, y2) {
      let ctx = this.canvas;
      ctx.beginPath();
      ctx.strokeStyle = 'black';
      ctx.lineWidth = 1;
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.stroke();
      ctx.closePath();
    },
    draw(e) {
      this.drawLine(this.x, this.y, e.offsetX, e.offsetY);
      this.x = e.offsetX;
      this.y = e.offsetY;
    }
  },
  // ...
})


Enter fullscreen mode Exit fullscreen mode

Don't forget to update the Vue directive mousemove to use draw: <canvas id="myCanvas" width="560" height="360" @mousemove="draw"/>

3_mousemove_draw

Now we're getting somewhere! While your cursor is within the canvas boundaries, Vue keeps creating a line from old x- and y-coordinates to the next location.

But did you notice the ugly line from the top left corner? That's because we set the default (x,y) coordinates to be (0,0). We want to fix it but not by modifying the default coordinates. Instead, we need to tell Vue when to start and stop drawing. Just like how a pen shouldn't be able to transfer ink to paper just by hovering, the mouse shouldn't be able to draw just by moving over the canvas.

4. mousedown, mouseup

Create a new state called isDrawing and set the default value to false. Then define two methods to handle mousedown and mouseup events. Update draw to use the isDrawing flag. It looks like we added a lot of stuff but we're simply telling Vue to draw if and only if the left mouse button is pressed.



new Vue({
  // ...
  data: {
    canvas: null,
    x: 0,
    y: 0,
    isDrawing: false
  },
  methods: {
    // ...
    draw(e) {
      if(this.isDrawing) {
        this.drawLine(this.x, this.y, e.offsetX, e.offsetY);
        this.x = e.offsetX;
        this.y = e.offsetY;
      }
    },
    beginDrawing(e) {
      this.x = e.offsetX;
      this.y = e.offsetY;
      this.isDrawing = true;
    },
    stopDrawing(e) {
      if (this.isDrawing) {
        this.drawLine(this.x, this.y, e.offsetX, e.offsetY);
        this.x = 0;
        this.y = 0;
        this.isDrawing = false;
      }
    }
  },
  // ...
})


Enter fullscreen mode Exit fullscreen mode

Pass the new functions to the appropriate Vue directives: <canvas id="myCanvas" width="560" height="360" @mousemove="draw" @mousedown="beginDrawing" @mouseup="stopDrawing" />. Then remove the coordinates from the dom to finish your project!

Clik here for the complete code.

Image of Datadog

The Future of AI, LLMs, and Observability on Google Cloud

Datadog sat down with Google’s Director of AI to discuss the current and future states of AI, ML, and LLMs on Google Cloud. Discover 7 key insights for technical leaders, covering everything from upskilling teams to observability best practices

Learn More

Top comments (1)

Collapse
 
seedyrom profile image
Zack Kollar • • Edited

This is not the correct way to get elements from inside a component, this is the the correct way:

<template>
     <div ref="referenceElement">
     </div>
</template>
<script>
export default {
    name: "FakeComponent",
    mounted() {
        this.$refs["referenceElement"] // This is the correct way to get the internal dom elems!
    }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Instrument, monitor, fix: a hands-on debugging session

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️