简体   繁体   中英

How to get usable canvas from Mapbox GL JS

I'm using Mapbox GL , and trying to get a snapshot of it, and merge the snapshot with another image overlaid for output.

I have a HTMLCanvasElement off screen, and I'm first writing the canvas returned from Map.getCanvas() to it, then writing the second (alpha transparent) canvas over that.

The problem is that, though I clearly see elements onscreen in the Map instance, the result only shows the second image/canvas written, and the rest is blank.

So I export just the map's canvas, and I see it is because the map canvas is blank, although a console.log() shows the image data from it to be a large chunk of information.

Here's my export function:

  onExport(annotationCanvas: HTMLCanvasElement) {

    const mergeCanvas: HTMLCanvasElement = document.createElement('canvas');
    const mapCanvas: HTMLCanvasElement = this.host.map.getCanvas();
    const mergeCtx: CanvasRenderingContext2D = mergeCanvas.getContext('2d');

    mergeCanvas.height = annotationCanvas.height;
    mergeCanvas.width = annotationCanvas.width;
    mergeCtx.drawImage(mapCanvas, 0, 0);
    mergeCtx.drawImage(annotationCanvas, 0, 0);

    const mergedDataURL = mergeCanvas.toDataURL();
    const mapDataURL = mapCanvas.toDataURL();
    const annotationDataURL = annotationCanvas.toDataURL();

    console.log(mapDataURL); // Lots of data

    download(mapDataURL, 'map-data-only.png'); // Blank image @ 1920x1080
    download(mergedDataURL, 'annotation.png'); // Only shows annotation (the second layer/canvas) data

  }

Is this a bug, or am I missing something?

UPDATE: I sort of figured out what this is about, and have possible options.

Upon stumbling upon a Mapbox feature request , I learned that if you instantiate your Map with the preserveDrawingBuffer option set to false (the default), you wont be able to get a canvas with usable image data. But setting this option to true degrades performance. But you can't change this setting after a Map is instantiated...

I want the Map to perform the best it possibly can!!!!

So, on this answer I stumbled on, regarding a question about three.js, I learned that if I take the screenshot immediately after rendering, I will get the canvas/data that I need.

I tried just calling this.host.map['_rerender']() right before I capture the canvas, but it still returned blankness.

Then searching around in the source code, I found a function called _requestRenderFrame , that looks like it might be what I need, because I can ask the Map to run a function immediately after the next render cycle. But as I come to find out, for some reason, that function is omitted in the compiled code, whilst present in the source , apparently because it is only in the master, and not part of the release.

So I don't really have a satisfactory solution yet, so please let me know of any insights.

As you mentioned in your updated question the solution is to set preserveDrawingBuffer: true upon Map initialisation.

To answer your updated question I think @jfirebaugh's answer at https://github.com/mapbox/mapbox-gl-js/issues/6448#issuecomment-378307311 sums it up very well:

preserveDrawingBuffer can't be modified on the fly. It has to be set at the time the WebGL context is created, and that can have a negative effect on performance.

It's rumored that you can grab the the canvas data URL immediately after rendering, without needing preserveDrawingBuffer , but I haven't verified that, and I suspect it's not guaranteed by the spec.

So although it might be possible to grab the canvas data URL immediately after rendering, it's not guaranteed by the spec.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM