Html2canvas: Exporting using Leaflet

Created on 9 Apr 2015  ·  21Comments  ·  Source: niklasvh/html2canvas

Has anyone attempted to use html2canvas with leaflet. I have been able to get html2canvas to work with google maps but I now need to get it to work for leaflet, when I pan the map left, right, up or down I am missing some of the tiles from leaflet. Also I no longer get my overlay. I have created a crude jsfiddle to show the missing tiles problem. Also attached is a screen shot from my code
missingtiles

https://jsfiddle.net/japiasente/7ybndo5L/

Needs More Information

Most helpful comment

It takes a few ugly hacks to get it to work. Here's a simple example without any fixes:

http://jsfiddle.net/djwbra47/

There are several problems:

  1. The circular marker gets clipped to a quarter-circle. It looks like html2canvas is clipping elements BEFORE any transforms are applied. The marker would be centered at 0,0 without any transformations, so it gets clipped to the quarter-circle that would be visible at that position. Then afterwards html2canvas transforms it to the actual location. I fixed it by setting the left and top margins to 0 instead of negative (so the entire marker would be visible at 0,0), then adjusting the transformations by the same amount. (And then change those values back after the canvas is grabbed, of course.)
  2. The line shows up in the wrong place. It looks like html2canvas is applying transformations on the SVG paths TWICE. Without any fixes, the line shows up too far to the upper left. If you change its transformation to 0, it shows up the same distance too far to the lower right. I had to set the transformation to half of the original values to get it to work.
  3. When you pan the map, the tiles get clipped to the original extent. This is the same issue as number 1: Leaflet pans the map by transforming the map pane, and html2canvas is clipping the tiles before that transform is applied. To fix, add the transform X and Y values to the left and top values of each img, then set their transforms to 0.
  4. When you pan the map, html2canvas draws the tiles on top of the marker and line so they're no longer visible. This is fixed when you make the changes in number 3, but I don't understand why. Maybe the drawing order changes when an element's properties are updated?

Here's the fixed version:
http://jsfiddle.net/oo2yms1h/

There's still one issue I haven't worked out yet: when you pan the map so the line is near the right edge, it doesn't show up on the canvas. But I'm guessing it's another result of html2canvas clipping before the transform.

One more note: The default leaflet marker is an image hosted from cdn.leaflet.com which gives a CORS error, meaning it won't show up on the canvas. Anyone using it with html2canvas would need to host the icon themselves, or switch to a different marker style.

JAPiasente, looks like my fix in number 3 can be adapted to resolve your tile and panning issues. Also, your markers are SVG paths and seem to be suffering from the same double-transform issue as my line in number 2, they're showing up too far to the upper left. Here's a version with those changes:

https://jsfiddle.net/6tn0zy48/

Everything shows up in the right place, except the markers are getting clipped further up than the bottom of the map. Looks like it's the same issue I'm still working on.

All 21 comments

Provide a example the problem using http://jsfiddle.net

It takes a few ugly hacks to get it to work. Here's a simple example without any fixes:

http://jsfiddle.net/djwbra47/

There are several problems:

  1. The circular marker gets clipped to a quarter-circle. It looks like html2canvas is clipping elements BEFORE any transforms are applied. The marker would be centered at 0,0 without any transformations, so it gets clipped to the quarter-circle that would be visible at that position. Then afterwards html2canvas transforms it to the actual location. I fixed it by setting the left and top margins to 0 instead of negative (so the entire marker would be visible at 0,0), then adjusting the transformations by the same amount. (And then change those values back after the canvas is grabbed, of course.)
  2. The line shows up in the wrong place. It looks like html2canvas is applying transformations on the SVG paths TWICE. Without any fixes, the line shows up too far to the upper left. If you change its transformation to 0, it shows up the same distance too far to the lower right. I had to set the transformation to half of the original values to get it to work.
  3. When you pan the map, the tiles get clipped to the original extent. This is the same issue as number 1: Leaflet pans the map by transforming the map pane, and html2canvas is clipping the tiles before that transform is applied. To fix, add the transform X and Y values to the left and top values of each img, then set their transforms to 0.
  4. When you pan the map, html2canvas draws the tiles on top of the marker and line so they're no longer visible. This is fixed when you make the changes in number 3, but I don't understand why. Maybe the drawing order changes when an element's properties are updated?

Here's the fixed version:
http://jsfiddle.net/oo2yms1h/

There's still one issue I haven't worked out yet: when you pan the map so the line is near the right edge, it doesn't show up on the canvas. But I'm guessing it's another result of html2canvas clipping before the transform.

One more note: The default leaflet marker is an image hosted from cdn.leaflet.com which gives a CORS error, meaning it won't show up on the canvas. Anyone using it with html2canvas would need to host the icon themselves, or switch to a different marker style.

JAPiasente, looks like my fix in number 3 can be adapted to resolve your tile and panning issues. Also, your markers are SVG paths and seem to be suffering from the same double-transform issue as my line in number 2, they're showing up too far to the upper left. Here's a version with those changes:

https://jsfiddle.net/6tn0zy48/

Everything shows up in the right place, except the markers are getting clipped further up than the bottom of the map. Looks like it's the same issue I'm still working on.

Fixing that last remaining problem wasn't hard, it was just a matter of setting the transform to 0 and using left and top instead.

My simple example, updated:
http://jsfiddle.net/oo2yms1h/1/

And my revised fix for your map:
https://jsfiddle.net/6tn0zy48/1/

I was working in Chrome, and I just noticed that my fix doesn't work in Firefox and IE. They were giving the transform styles as translate instead of translate3d, and my code assumed it would always be translate3d. And they positioned the tiles using translate instead of left and top like in Chrome. This updated example is revised for all three browsers. Tiles and marker icons work in all three, but I still can't get the SVG paths to show up in Firefox and IE.

http://jsfiddle.net/oo2yms1h/3/

IE needed html2canvas.svg before it would show the line, and it also doesn't work when transforms or left/top properties are set on the SVG layer. This example works in IE, and almost works in Firefox:

http://jsfiddle.net/oo2yms1h/5/

To get the line to show in firefox, you also need to switch to base64 encoding by making the change to html2canvas mentioned in #648:
https://github.com/niklasvh/html2canvas/pull/648/files

Thanks for all your help so far. The only last issue now is if I have an svg overlay ontop of it, IE isnt playing nicely with it. The map and the svg overlay are working nicely in chrome. Any ideas?

@CraigVA You are amazing. I just spent the last two days trying to fix this issue. I wish I had come across your jsfiddles earlier. Thanks!!!

I had an issue using @CraigVA solution where if I pan the map at all, the overlays would be off center from the tiles.

To combat it, all I did was a redraw() function that set the view to some other place in the ocean for a second and then set the view back to where it was. Works fine.

function redraw() {
    var lat_tmp = map.getCenter().lat;
    var lng_tmp = map.getCenter().lng;
    map.setView([-66.22149259832975, -1.142578125]);
    setTimeout(function () {
        waitForTilesToLoad()
    }, 50000);
    map.setView([lat_tmp, lng_tmp]);
}

In case anyone comes across this and has the same issue.

Hi, thanks for this hack, it works well except for Path layers and for big TileLayers.

  • If a part of a Path layer (polygon/polyline) is out of orginal map boundaries, it is clipped by html2canvas when I move the map.
    I updated your code here http://jsfiddle.net/c8LL4qfo/ with a longer polyline. Try to move the map, you'll see that the line is clipped in the canvas.
    Any ideas about how to solve this bug ?

  • Second point : if tileLayers are too big html2canvas is not waiting for the map to render. Even with UseCors=true. Is there any way to wait for the tileLayers loading ? I was thinking about using the load event, but I have no idea how I could.

thanks

Is this still an issue with v1.0.0? If so, could you please share an example on jsfiddle.

I solved this problem using React : the leaflet component is reset after moving the map, keeping in state the last center and zoom level. There's no more conflict with html2canvas as the provided map is a new map.

Ok, I made the update in the jsfiddle code here : http://jsfiddle.net/2zkLkLxc/
There is no more clipping problems with V1.0.0 but we have back problems with icons clipping (anytime) and tiles clipping when moving the map.
I tried to remove the hacks on tiles and it doesn't change anything.

It's strange, because I have updated to v.1.0.0 in my code and I don't have icons clipping.

This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further.

I have the same problem, but I used Leaflet Map instead of Google Map.
The code is below

var transform=$(".leaflet-map-pane").css("transform");
if (transform) {
var c = transform.split(",");
var d = parseFloat(c[4]);
var h = parseFloat(c[5]);
$(".leaflet-map-pane").css({
"transform": "none",
"left": d,
"top": h
})
}
html2canvas(document.body).then(function(canvas){
$(".leaflet-map-pane").css({
left: 0,
top: 0,
"transform": transform
})
} // Here is used html2canvas 1.0.0-alpha.9

@CraigVA Thanks a LOT!

I have an issue at the moment up on stackoverflow regarding the clipping problem. I'm using leaflet 1.3.1. If someone can provide me some guidance, it would be much appreciated. The issue is here.

@niklasvh The issue is still there: http://jsfiddle.net/x3jzsg9b/4/

The issue its still there: https://jsfiddle.net/x512pgjt/269/

@amarandon @bomba1990

Try to remove de renderer padding from your map. It worked for me.

map.getRenderer(map).options.padding = 0;

This worked lovely!

html2canvas(document.querySelector("#mapid"), {
allowTaint: true,
useCORS: true
}).then(canvas => {
document.body.appendChild(canvas)
});

Was this page helpful?
0 / 5 - 0 ratings