Chris Kinniburgh
Chris Kinniburgh

Reputation: 392

How can I make an HTML5 canvas display HTML?

I realize a canvas cannot directly render HTML. However, it seems like there are potential workarounds. I don't need the HTML to render perfectly, but I'd like at the very least an image of the rendered HTML.

To that end, I attempted to convert the HTML to SVG. This works with very basic html, but breaks with many use cases (such as including images in the html):

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = new Image();

var data =  '<svg xmlns="http://www.w3.org/2000/svg" width="' + screen.width + '" height="' + screen.height + '">' +
            '<foreignObject width="100%" height="100%">' +
            '<div xmlns="http://www.w3.org/1999/xhtml">' +
            document.body.innerHTML + 
            '</div></foreignObject></svg>'

var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);

img.onload = function () {
    ctx.drawImage(img,0,0,c.width,c.height);
    DOMURL.revokeObjectURL(url);
}
img.src = url;

Is there a simpler way of getting html displaying correctly on a canvas?

Upvotes: 4

Views: 2881

Answers (2)

IsaacDorenkamp
IsaacDorenkamp

Reputation: 61

You could potentially create a basic recursive function to descend into the children of the <canvas> element that uses a switch or an event handler object to handle certain elements. I demonstrate the former here:

var canvas = document.getElementById('canvas');
var ctx    = canvas.getContext('2d');

ctx.clearRect( 0, 0, canvas.width, canvas.height );

var y = 50; //Set to largest text
var offset = 25;

(function descend(parent){
  for( var i = 0; i < parent.childNodes.length; i++ ){
    var child = parent.childNodes[i];
    
    var name = child.tagName;
    var val  = child.innerHTML;
    
    if( child.hasChildNodes() ) descend(child);
    
    switch( name ){
      case 'H1':
        ctx.font = "normal 48px Arial";
        y += 25;
        ctx.fillText( val, offset, y );
        y += 25;
        break;
      case 'H2':
        ctx.font = "normal 36px Arial";
        y += 20;
        ctx.fillText( val, offset, y );
        y += 20;
        break;
      case 'H3':
        ctx.font = "normal 24px Arial";
        y += 12;
        ctx.fillText( val, offset, y );
        y += 12;
        break;
      default:
        break;
    }
  }
})( canvas );
<canvas id="canvas" width="300" height="300"><h1>H1</h1><h2>H2</h2><h3>H3</h3></canvas>

This code utilizes a function to descend into the element tree of the element and handles certain elements with different rendering techniques. It may seem a little bit excessive, but if you are looking for an effective way to achieve that goal, this method works. Especially with the flexibility of rendering, that being your ability to choose how to render which elements how you want to, so if you want to render all <span>'s red for some reason, you can do that. The function I provided is extremely basic, only rendering <h1>-<h3>, and when you start to mix them around inside the , it gets a little "render jerky". But it demonstrates the concept well, and I think you may find it effective.

Although the thoughts posted in the comments are definitely admirable, if you are looking for flexibility or would rather have control over rendering, this may be the solution you are looking for.

Note: I will say that the code does not detect updates in the <canvas> element's DOM; you will need a mutation observer for that. You might also wish to place the code inside of a function that is run when the page is loaded, so that the JavaScript runtime can actually find your <canvas> element.

Upvotes: 1

lorefnon
lorefnon

Reputation: 13115

Potential solutions:

This script allows you to take "screenshots" of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.

dom-to-image is a library which can turn arbitrary DOM node into a vector (SVG) or raster (PNG) image, written in JavaScript.

Upvotes: 0

Related Questions