如何从Mapbox GL JS获取可用的画布 [英] How to get usable canvas from Mapbox GL JS
问题描述
我正在使用 Mapbox GL ,并尝试获取它的快照,并将快照与另一个覆盖输出的图像合并。
我有一个 HTMLCanvasElement
关闭屏幕,我'我首先将从 Map.getCanvas()
返回的画布写入其中,然后将第二个(alpha透明)画布写在上面。
问题在于,虽然我清楚地看到Map实例中的屏幕上的元素,但结果只显示第二个图像/画布,其余的是空白。
所以我只导出了地图的画布,我看到它是因为地图画布是空白的,尽管 console.log()
显示了它的图像数据这是我的导出功能:
onExport(annotationCanvas:HTMLCanvasElement){
const mergeCanvas:HTMLCanvasElement = document.createEl EMENT(画布);
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); //大量数据
下载(mapDataURL,'map-data-only.png'); //空白图片@ 1920x1080
下载(mergedDataURL,'annotation.png'); //仅显示注释(第二层/画布)数据
}
这是一个错误,还是我错过了什么?
更新:我想知道这是什么,并有可能的选择。
绊倒了 Mapbox功能请求,我了解到,如果您使用 preserveDrawingBuffer
选项设置为实例化 Map
false
(默认值),您将无法获得包含可用图像数据的画布。但是将此选项设置为 true
会降低性能。但是在实例化Map之后你无法改变这个设置......
我希望Map能够尽可能地发挥最佳效果!!!!
所以,在这个答案上我偶然发现了关于关于three.js的问题,我了解到如果我在渲染后立即拍摄截图,我将得到我需要的画布/数据。
我试过了在捕获画布之前调用 this.host.map ['_ rerender']()
,但它仍然返回空白。
然后在源代码中搜索,我发现了一个名为 _requestRenderFrame
的函数,看起来它可能是我需要的,因为我可以要求Map运行紧接在下一个渲染周期之后的函数。但是,当我发现由于某种原因,该功能是在编译的代码中省略,但在源中出现,显然是因为它只是在主人身上,而不是释放的一部分。
所以我还没有一个满意的解决方案,所以请让我知道任何见解。
正如您在更新的问题中所提到的,解决方案是设置 preserveDrawingBuffer:true
在地图初始化时。
为了回答您的更新问题,我认为@ jfirebaugh的答案在 https://github.com/mapbox/mapbox-gl-js/issues/6448#issuecomment-378307311 总结一下非常好:
无法动态修改preserveDrawingBuffer。必须在创建WebGL上下文时进行设置,这可能会对性能产生负面影响。
有传言说你可以在渲染后立即获取画布数据URL,而不需要preserveDrawingBuffer,但我还没有验证,我怀疑它不符合规范。
因此,尽管可能在渲染后立即抓取画布数据URL,但不能保证规范。
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.
这篇关于如何从Mapbox GL JS获取可用的画布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!