Search code examples
javascriptcssvue.jshtml5-canvas

Responsive canvas vuejs


I'm trying to make my canvas fit its container, in a vue component. I fire resizeCanvas() in mounted, but the container height and width are 0. How can I have a canvas that fits its container, so that I can change the container size using css and it will update the canvas ? (Sorry for my english, i'm not fluent)

<template lang="html">
  <div class="container" ref="container">
    <canvas ref="canvas" :width="width" :height="height"></canvas>
  </div>
</template>

<script>

export default {
  name: "monster",
  data ()
  {
    return {
      width: 512,
      height: 512
    }
  }
  methods: {
    resizeCanvas(){
      console.log(this.$refs.container.offsetWidth); // logs 0
      this.width = this.$refs.container.offsetWidth
      this.height = this.$refs.container.offsetWidth
    }
  },
  mounted ()
  {
    console.log(this.$refs.container.offsetWidth) // logs 0
    window.addEventListener("resize", this.resizeCanvas);
    this.resizeCanvas()  
  },
  unmounted() {
    window.removeEventListener("resize", this.resizeCanvas);
  },
}
</script>

<style lang="css" scoped>
.container {
  width: 100%; height: 100%
}
</style>

Solution

  • The mounted lifecycle hook in VueJS does not guarantee that the DOM is ready: you will need to wait for this.$nextTick() and then check the dimensions of your container element.

    You can do it by moving the logic into the callback of nextTick, i.e.:

    mounted () {
        this.$nextTick(() => {
            console.log(this.$refs.container.offsetWidth);
            window.addEventListener("resize", this.resizeCanvas);
            this.resizeCanvas();
        });
    },
    

    If you're familiar with the async/await way of doing things, then you can also do this:

    async mounted () {
        await this.$nextTick();
    
        console.log(this.$refs.container.offsetWidth);
        window.addEventListener("resize", this.resizeCanvas);
        this.resizeCanvas();
    },
    

    Another possibility is to delegate this "wait for DOM to be ready" responsibility to the resizeCanvas function, so you have full abstraction and separation of concerns:

    methods: {
        async resizeCanvas(){
          await this.$nextTick();
          
          this.width = this.$refs.container.offsetWidth
          this.height = this.$refs.container.offsetWidth
        }
    },
    mounted () {
        window.addEventListener("resize", this.resizeCanvas);
        this.resizeCanvas();
    },