How to use html2canvas with Vue?

I’m trying to use html2canvas to capture a section of my template. This is the summary of my markup:

<template>
   <div>
      <md-layout>
         <div id="capture" class='m1 '>
            // more markup...
         </div>
      </md-layout>
   </div>
</template>

Vue doesn’t have a shorthand for document.getElementById() and in my experience it isn’t a good practice to mix Vue with vanilla JS, so I implemented this function to try to get the capture id reference:

showCaptureRef() {
  console.log("this.$refs.capture: " + this.$refs.capture);
  let vc = this;
  return vc.$refs.capture;
},

and use it here:

downloadVisualReport () {
  let vc = this
  alert("Descargando reporte visual")
  console.log('campaign-view#downloadVisualReport');
  html2canvas(vc.showCaptureRef()).then(canvas => {
      vc.document.body.appendChild(canvas)
  }).catch((error) => {
    console.log("Erorr descargando reporte visual")
    alert("Error descargando el reporte visual")
  });
},

I’m getting a Cannot read property ‘ownerDocument’ of undefined error, so I guess that there must be an error in the showCaptureRef()function.

there must be an issue somewhere. Are you using ref on a component? Or raw html? Can you show us the console.log output of your $refs.capture ?

https://jsfiddle.net/dw5efkfn/

Here is my fiddle which seems to work.

1 Like

Is it working for you? I’m getting the error alert when running your fiddle.

When logging: this.$refs.capture: undefined

I’m using ref in a div that has some vue-material components inside, but no children components.

Check that fiddle again, I updated it, it works.

Can you show us your template whree your ref is defined? Clearly your ref is not available at that moment in time, thats a big clue right there when seeing undefined, so of course it will not work passing undefined into the html2canvas method

bolerodan thank you so much for your attention.

I’ve ran the fiddle again and it’s not downloading the image. What browser are you using?

This is the part of the template where ref is defined:

<div id="capture" class='m1'>

  <md-button v-on:click='changeCampaignStatus(campaign.id, "Paused")' v-if='campaign.state==="Running"' class='md-icon-button md-accent md-raised'>
    <md-icon>pause</md-icon>
  </md-button>

  <md-button v-on:click='changeCampaignStatus(campaign.id, "Running")' v-if='campaign.state==="Paused"' class='md-icon-button md-accent md-raised' :disabled="pausedFor5Seconds">
    <md-icon>play_arrow</md-icon>
  </md-button>

  <md-button v-on:click='changeCampaignStatus(campaign.id, "Canceled")' v-if='campaign.state==="Paused"' class='md-icon-button md-accent md-raised'>
    <md-icon>stop</md-icon>
  </md-button>

  <md-button v-on:click='deleteCampaign(campaign.id)' v-if='campaign.state==="Canceled"' class='md-icon-button md-accent md-raised'>
    <md-icon>delete_forever</md-icon>
  </md-button>

  <md-button v-on:click='duplicateCampaign(campaign.id)' class='md-icon-button md-accent md-raised'>
    <md-icon>content_copy</md-icon>
  </md-button>

  <md-button v-on:click='goToEdit'  v-if='campaign.state==="Paused"' class='md-icon-button md-accent md-raised'>
    <md-icon>mode_edit</md-icon>
  </md-button>
  <md-button v-on:click='cleanQueue'  v-if='campaign.state==="Paused"' class='md-icon-button md-accent md-raised'>
    <md-icon>error</md-icon>
  </md-button>
  <div>
    <md-chip v-for='tag in campaign.tags' :key='tag' class='mr1'>{{tag}}</md-chip>
  </div>
</div>

Im using Chrome, appends to the body fine.

Are you sure thats where Ref is defined? I see no intsances of ref="capture" in that template you posted.

Hmmm that fiddle’s behavior is so weird…

Anyway, I see I had id='capture' instead of ref="capture". Changed it and now html2canas is logging some stuff on the console:

this.$refs.capture: [object HTMLDivElement]
Logger.js:36 0ms html2canvas: html2canvas $npm_package_version
Logger.js:36 427ms html2canvas: Document cloned, using computed rendering
Logger.js:36 428ms html2canvas: Starting node parsing
Logger.js:36 440ms html2canvas: Finished parsing node tree
Logger.js:36 441ms html2canvas: Finished loading 0 images []length: 0__proto__: Array(0)concat:…
Logger.js:36 442ms html2canvas: Starting renderer
Logger.js:36 444ms html2canvas: Canvas renderer initialized (52x58 at 349.21875,220) with scale 1
Logger.js:36 457ms html2canvas: Render completed
campaign-view.vue:536 Erorr descargando reporte visual

But as you can see, the error alert is being thrown still.

Not sure if it’s solved or not… Do you know if from this point I can get to download the rendered image?

Okay excellent. You’re getting there. Remember, be as verbose as possible when debugging. If you console.log the actuall error message returned (instead of just a basic message), it would give you a hint of whats going on (even paste here). (This is how I debugged your code to make it work in my fiddle)

this line

vc.document.body.appendChild(canvas)

Change to

document.body.appendChild(canvas)

that should work.

OMG, that was it. Thank you so much for all your help bolerodan!

Is your screenshot also picking up the image or sort sort of url() image. I am trying to use html2canvas but it only takes .png Image that is in relative path to the file. Not picking up any url() image. It’s so annoying :neutral_face::neutral_face::neutral_face:

Nice fiddle! Thanks for it.

I am having a problem with cors. I added the code below at the line 7 of your fiddle, but it does not work.

<img src="https://books.google.com/books/content?id=eQkJAQAAMAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api" />

Do you know how to solve it?