html2canvas and google map v3.33

Created on 20 Jun 2018  路  8Comments  路  Source: niklasvh/html2canvas

Hello!
I am facing a problem exporting google maps with polylines into an image.
I manage to reproduce the error in the following case:
after moving the map viewport, if the polyline does not appear complete in the map viewport, then the printed image does not include the polyline at all, with the latest gmaps api v.3.33.

Tested in:
html2canvas: html2canvas 1.0.0-alpha.12
Browser & version: Firefox Quantum 60.0.2 (64-bit)
Operating system: Win 7

Applying the following transformation:
var mapNodes = '.gm-style>div:first>div:first>div:last>div';
var transformer = $(mapNodes).css('transform');
var comp = transformer.split(',');
var mapLeft = parseFloat(comp[4]);
var mapTop = parseFloat(comp[5]);
$(mapNodes).css({
'transform': 'none',
'left': mapLeft,
'top': mapTop
});
and using the google map api v.3.31 (as suggested from some posts) works, but i need to use the newest google maps version (v.3.33) with the different renderer, and this solution doesn't apply anymore. I also tried with this selector: '.gm-style>div:first>div', but still not solved.

Below an example:

The map looks like that:
map

but printed like that:
printedmap

Any ideas how to solve the problem for html2canvas and google maps v.3.33?
I would appreciate any hint!
Thank you!

Most helpful comment

This selector '.gm-style>div:first>div:first>div:last>div' is for the Google map tile layer.
I also use html2canvas with drawing overlays.
This drawing overlay tile layer is in a different div. If you use your browser's inspector tool, you'll see other div's in '.gm-style>div:first>div:first' div. Once you find other divs with a transform css component, you can start troubleshooting which div is the correct one.
You'll have to do the transform on each div that needs a transformation. For my project, the drawing layer was in '.gm-style>div:first>div:first>div:nth-child(2)>div:first>div'.
Hope this helps.

All 8 comments

This selector '.gm-style>div:first>div:first>div:last>div' is for the Google map tile layer.
I also use html2canvas with drawing overlays.
This drawing overlay tile layer is in a different div. If you use your browser's inspector tool, you'll see other div's in '.gm-style>div:first>div:first' div. Once you find other divs with a transform css component, you can start troubleshooting which div is the correct one.
You'll have to do the transform on each div that needs a transformation. For my project, the drawing layer was in '.gm-style>div:first>div:first>div:nth-child(2)>div:first>div'.
Hope this helps.

Try this:
Transform to/fro function to be called before/after html2canvas rendering:

function transform(div) {
//get transform value
var transform=$(div).css("transform");
var comp=transform.split(","); //split up the transform matrix
var mapleft=parseFloat(comp[4]); //get left value
var maptop=parseFloat(comp[5]); //get top value
$(div).css({ //get the map container. not sure if stable
"transform":"none",
"left":mapleft,
"top":maptop,
});
}
function transformBack(div) {
var transform=$(div).css("transform")
$(div).css({
left:0,
top:0,
"transform":transform
});
}

call it like this (I've found these three selectors work to keep the map shift in order, keep any directions lines in place, and keep any markers or overlays in place):
transform($('.gm-style>div:first>div:first>div:last>div')[0]);
transform($('.gm-style>div:first>div:first>div:first>div:first>div')[0]);
transform($('.gm-style>div:first>div:first>div:nth-child(2)>div:first>div')[0]);

transformBack($('.gm-style>div:first>div:first>div:last>div')[0]);
transformBack($('.gm-style>div:first>div:first>div:first>div:first>div')[0]);
transformBack($('.gm-style>div:first>div:first>div:nth-child(2)>div:first>div')[0]);

You could also just parse the entirety of your map for any divs that have a transform with non-zero values in the matrix- something like:
var divsToTransform = $('#map-selector div').filter(function() {
return $(this).css('transform').startsWith('matrix');
});

Try to Use this FIX but for me it's not work FF and Chrome same result with last release :
fix try 1: https://jsfiddle.net/denis_miroshnikov_ts/erjvu371/3/

help me please , I think I do something wrong..

@DenisKS
Your selector needs to change- use this instead

var transform = $(".gm-style>div:first>div:first>div:last>div").css("transform");
var comp = transform.split(","); //split up the transform matrix
var mapleft = parseFloat(comp[4]); //get left value
var maptop = parseFloat(comp[5]); //get top value
$(".gm-style>div:first>div:first>div:last>div").css({ //get the map container. not sure if stable
"transform": "none",
"left": mapleft,
"top": maptop,
});

Solution suggested from mylesboone solved the problem!
Thank you very much @mylesboone !

Amazing. I was days looking for the solution to the gray areas generated in the maps when exporting to png by html2canvas, and also looked for to keep the correct location within the div for both the map layer and the upper one with rectangles.
The tip passed by @mylesboone worked perfectly to adjust the layers to export as PNG. Thank you so much.

The issue with blank map or an error generating the canvas was tricky, but eventually what fixed it for me was adding this config:

ignoreElements: (node) => {
        return node.nodeName === 'IFRAME';
      }
html2canvas(mapWrapper, {
      useCORS: true,
      allowTaint: false,
      ignoreElements: (node) => {
        return node.nodeName === 'IFRAME';
      }
    }).then(canvas => {
      const url = canvas.toDataURL('image/png');
      saveAs(url, 'image3.png');
      window.URL.revokeObjectURL(url);
    });

I made more universal code, which can find required blocks, change transform, replace the map with image and then restore the map

function removeTransform(elements, styles) {
  let parts;
  for (let i = 0; i < styles.length; i++) {
    parts = styles[i].split(",");
    const mapLeft=parseFloat(parts[4]);
    const mapTop=parseFloat(parts[5]);
    $(elements[i]).css({
      transform: "none",
      left: mapLeft,
      top: mapTop,
    });
  }
}

function restoreTransform(elements, styles) {
  for (let i = 0; i < styles.length; i++) {
    $(elements[i]).css({
      left: 0,
      top: 0,
      transform: styles[i]
    });
  }
}

const mapElement = $('.gmap')[0];
const mapParent = $(mapElement).parent()[0];

const elements = $(mapElement).find('div')
  .filter(function(){return $(this).css('transform').includes('matrix') && !$(this).width();});
const styles = [];
for (let i of elements) {
  styles.push($(i).css('transform'));
}

this.removeTransform(elements, styles);

const options = {backgroundColor: null, useCORS: true,
  ignoreElements: (node) => {
    return node.nodeName === 'IFRAME';
  }
};
//convert map to image and swap them 
html2canvas(mapElement, options).then( canvas => {
  const image = document.createElement('img');
  image.src = canvas.toDataURL("image/png");
  image.style.width = '100%';
  mapParent.removeChild(mapElement);
  mapParent.appendChild(image);

  this.restoreTransform(elements, styles);


  setTimeout(() => {
    //do something with page

    //restore map
    mapParent.removeChild(image);
    mapParent.appendChild(mapElement);
    image = null;
  }, 200);
});
Was this page helpful?
0 / 5 - 0 ratings