One of my current projects — GitShoes was pretty straightforward. However, we quickly realized that users didn’t usually provide enough feedback for a developer to easily solve the problem. A few words (especially from a non-technical user) might not lead the developer to the root of the problem, so we decided to take things one step further.
“A picture is worth a thousand words.”
Our average issue was about 20-30 words long. So, if a picture is worth 1000 words, that means taking some quick screenshots at issue submission would make our app about 40x more useful. Seems like a no-brainer feature to me. But how do we make it happen?
Well, that would have been convenient. Fortunately, HTML5 introduced the Canvas element. Here’s W3C’s explanation of the Canvas element:
The canvas sounds like the perfect place for our screenshot to live, but how can we draw on it? I knew I couldn’t be the first person with a need for this, so I did what any reasonable developer would do: look to Open Source for a solution.
- HTML2Canvas is entirely client-side. Everything it does happens within your user’s browser, not on your server. This fact comes with its pros and cons. Pro: Repeatedly performing these operations on your own server would be very costly, but you don’t have to. Con: Your user’s browser environment is less reliable than your server’s. There’s a greater probability of something going wrong. You also need to send the image back in the form of a data url, which assumes a fairly powerful connection for the client.
- HTML2Canvas is recreating elements from the page (or the whole page) from the DOM. You must have a canvas available on the page in which to store the elements you recreate.
- HTML2Canvas only writes to the canvas. If you need to send your image somewhere, like your server, it’s up to you to handle that.
Writing to the canvas
The first step to consider is when you want your screenshot to be taken, likely either when the DOM is fully loaded or when something is clicked. For me, this was on clicking the widget, indicating you want to provide feedback. You’ll want to work within an event listener for that action.
1 2 3
Next, you must determine what you would like to take a screenshot of — the entire document or a select element. I chose to use the entire document.
1 2 3 4 5
Now, all that’s left is to tell HTML2Canvas where to place our newly canvased screenshot.
1 2 3 4 5 6 7 8 9 10 11
In the above code, I am doing a few things. First, I am removing any canvas I may have generated earlier in the pageview. Otherwise, each consecutive screenshot will take twice as long as the previous because it is recreating the existing canvas, even though it is hidden. Second, I am appending the newly created canvas to the body and applying CSS styles to hide it (so users don’t see a duplicate of their screen below) and make it easily findable. Lastly, I am revealing the feedback widget, but that isn’t relevant to the rest of the canvasing function. If you don’t need to hide your canvas or remove the existing canvas, you could get by without lines 4, 6, 7, 8 and the second half of line 5.
Converting the canvas to a data url
HTML is the language of the browser. If you plan on sending the screenshot back to your server or to a third-party, like AWS, it should be converted to something more manageable, like a data url (base64 encoded data). This is as simple as one line of code.
The data url we created from the canvas is ready to be sent back to the server. Once you have the image on your server, there are countless things you can do with it. I chose to store it in a tempfile and upload it to AWS using Fog, which is a great, lightweight cloud services gem, but that’s beyond the scope of this blog post. After being uploaded to AWS, the screenshots generated from the code above look like this.
Go forth and take some screenshots!
For such a powerful tool, HTML2Canvas is relatively easy to implement.
- Check out the documentation
- Find a way to add value to one of your projects through screenshots
- Be responsible — don’t expose sensitive information from your users