专栏首页ArcGIS JS API开发html2canvas实现ArcGIS API for JavaScript 4.X截图功能

html2canvas实现ArcGIS API for JavaScript 4.X截图功能

主要介绍ArcGIS API for JavaScript 4.X实现地图截图的两种方式,解决普通地图截图是底图空白的问题,最终效果如下:

需求描述

在我们项目开发过程中,有时候需要将地图上面绘制的元素或添加的一些图标之类的小元素进行截图保存或者展示,这时候就需要我们实现关于地图的截图功能。目前在ArcGIS API for JavaScript中其实已经提供了地图截图的API,但是该API对地图底图和一些自定义的需求支持度并不高,所以我们平时项目开发时建议使用第三方截图模块,今天就给大家介绍下关于地图截图的两种方式,最终效果如下:

实现方法

一、ArcGIS API for JavaScript自带的截图方式

介绍的第一种方式就是ArcGIS API for JavaScript自带的“esri/widgets/Print”截图微件去做,具体代码如下:

const print = new Print({
  view: view,
  printServiceUrl:
     "https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task"
});

view.ui.add(print, {
  position: "top-left"
});

微件截图的使用其实很简单,只需要几行代码就可以搞定,但是随之而来的问题就是自由度并不高,因为它已经自带了一份写好的微件UI,如下图:

在我们正常的项目开发中其实API自带的UI我们基本是不用的,需要跟着公司UI设计稿来做,所以我们就需要自己写UI,写完UI之后背后的交互逻辑就可以借鉴API自带的逻辑了,这个时候可以用到“esri/widgets/Print”这个API底层使用的一个API模块“esri/tasks/PrintTask”来做了,具体的实现代码如下:

const { view } = props;
const [PrintTask, PrintTemplate, PrintParameters] = await loadModules(['esri/tasks/PrintTask', 'esri/tasks/support/PrintTemplate', 'esri/tasks/support/PrintParameters'], gconfig.arcgis_options);
let printTask = new PrintTask({
    url: 'https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task'
});

let template = new PrintTemplate({
    format: "pdf",
    exportOptions: {
        dpi: 300
    },
    layout: "a4-portrait",
    layoutOptions: {
        titleText: "pdf标题",
        authorText: "作者信息"
    }
});

let params = new PrintParameters({
    view: view,
    template: template
});

printTask.execute(params).then((printResult) => {
    if (printResult.url) {
        message.success('pdf报告生成成功');
        window.open(printResult.url);
    } else {
        message.error('pdf报告生成失败');
    }
});

上述代码中大家可以看到,我们借用PrintTask来自己实现了一个截图功能,截图所需的各类参数直接在代码中写好了,你也可以将你自己写的UI界面的用户输入值传递到我们定义的打印模板信息中去实现用户自定义打印,这一块的代码较简单,就不给大家介绍了。

接下来我们说说这种方式实现截图的问题:

具体的一些细小的问题的话大家可以自己手动尝试去观察,无非就是一些涉及到跨域啊,参数值输入不一致形成的图片变形之类的,但是最大的一个问题就是:当我们的底图如果不使用ArcGIS自带的底图,这种方式实现截图的话会造成底图丢失。解决方法的话目前我也没时间去处理,所以直接采用了第二种实现方式,就是纯前端实现截图,摆脱ArcGIS技术体系。

二、html2canvas实现地图截图

摆脱了ArcGIS的技术体系,跳到整个大前端的领域再看截图这个功能的话,其实是一个很简单的问题,无非就是将所要截取的DOM节点转换为图片这样一个需求,所以我们就找到了html2canvas这个插件模块,选择它主要是参考文档较多,而且它的github活跃量较高,所以不担心一时半会出现停止维护的情况,其中最主要的就是它提供了npm下载引入方式,接下来就看看如何去实现。

html2canvas的官网信息大家可以看一下,其实使用很简单,就是下述几行代码:

npm install html2canvas  //安装

import html2canvas from 'html2canvas';  //引入

//使用
html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

上述四行代码就完成了安装、引入、使用三个环节,是不是很简单,除了通过import模块化加载之外,还提供<script>标签的普通加载引入,看大家需求。上述使用环节的代码大致思路就是将我们所要截取的DOM节点传入到html2canvas()这个的方法作为第一个参数,这个方法提供第二个参数,就是定义一些截图时的参数,根据需要大家可以根据官网介绍添加一些所需参数,然后在方法的then()回调里面我们就可以拿到截取之后的元素,此时的元素是一个canvas的DOM节点,我们可以直接将它添加到所要展示的区域或者将它转成图片直接打印输出。

介绍完html2canvas的一些基本信息之后,我们就来看看如何用它来实现我们的地图截图。html2canvas实现地图截图其实很简单,因为我们通过ArcGIS API for JavaScript实例化地图的时候需要传入一个存放和展示地图的div,如下:

const map = new Map({
  basemap,
});
const view = new SceneView({
  container: 'map-view',   //地图div的id值
  map,
});

所以我们截图的时候只需要通过js原生获取DOM节点的方式通过id获取到这个div,然后将它传入html2canvas()这个方法即可,最后在它的回调函数里面拿到截图,如下:

const element = document.getElementById('map-view');
const options = {};
html2canvas(element, options).then((canvas) => {
    const png = canvas.toDataURL("image/png");  //拿到截图后转换为png图片

    const img = document.createElement('img');
    img.setAttribute('src', png);
    window.document.body.appendChild(img);   //将png图片添加到页面验证
    console.log(png)
});

通过上述方式我们拿到了截图,然后将其添加到了页面上,但是当我们去查看页面的时候发现底图并没有截到,只有这样一个空白界面:

这就让人很是惊讶了是不是,我们查看控制台,也并没有报错,但是仔细观看的话会有这样一行警告信息:

#1 133ms Unable to clone WebGL context as it has preserveDrawingBuffer=false <canvas style=​"width:​ 100%;​ height:​100%;​ display:​block;​" width=​"1023" height=​"601">

然后我们分析整个页面结构可以发现一些端倪,如下图所示:

在通过ArcGIS API for JavaScript 4.X版本实例化地图的时候,我们的底图是通过canvas元素绘制出来的,它并不是之前3.X通过svg的形式绘制的,这就意味着html2canvas在截取的元素中已经包含有另一个canvas元素。结合告警信息不难猜出,ArcGIS API for JavaScript 4.X绘制的canvas元素的绘制句柄肯定是人家做了一定的限制,就想告警信息提示一样,它里面的preserveDrawingBuffer这个属性值是false,这就导致了截图时底图空白的问题,因为html2canvas截图的思路就是将所传入的DOM节点转换为canvas,但是既然传入的元素里面已经包含了一个canvas的话,它内部的转换逻辑肯定就会出错了,那怎么解决这个问题呢?谷歌和百度出来的资料都是千篇一律,说是在html2canvas()这个方法中增加配置信息,例如下面这些:

const options = {
      useCORS: true,
      // preserveDrawingBuffer: true,
      //foreignObjectRendering: true,
      allowTaint: true,
  };

但其实这种解决方式可能只针对于ArcGIS API for JavaScript 3.X版本实例化出来的底图空白问题有效,并不能结局4.X版本出现的问题,所以我们就来看看针对4.X版本如何解决这一问题。既然告警信息中提示了preserveDrawingBuffer属性值为false,那我们只需要将其设置为true,应该就可以解决,按照这个思路,网上又是一顿搜索操作,最后在Stack Overflow找到了解决方法,大家有兴趣的话可以看看:Canvas toDataURL() returns blank image

其实就是在我们地图实例化的后面,增加一个立即执行函数,在函数里面将preserveDrawingBuffer属性值设置为true即可,如下:

const map = new Map({
  basemap,
});
const view = new SceneView({
  container: 'map-view',
  map,
});
props.setMapView(view);

//解决html2canvas截图空白问题
HTMLCanvasElement.prototype.getContext = function (origFn) {
  return function (type, attributes) {
    if (type === 'webgl') {
      attributes = Object.assign({}, attributes, {
        preserveDrawingBuffer: true,
      });
    }
    return origFn.call(this, type, attributes);
  };
}(HTMLCanvasElement.prototype.getContext);

增加上述代码之后,我们发现截图正常,这样就实现了一个地图截图功能了,以上推荐的就是关于截图空白的最简单的解决方法,其实还有另一种思路:既然传入html2canvas()方法中的元素中包含有另一个canvas元素导致的底图空白,那我们可以在截图之前先将这个canvas转换为一个img标签的DOM节点替换掉现有的canvas,然后再截图,这样其实也可以解决此问题,但是这种方式可能对于动手能力不高的小伙伴来说就不太愿意了,大家有兴趣的话可以尝试一下。

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:http://www.xbeichenbei.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • ArcGIS API for JavaScript开发入门必读

    ArcGIS API for JavaScript开发必读的一篇入门文档,文章中对ArcGIS API for JavaScript做了简单的介绍,包括学习路线...

    X北辰北
  • ArcGIS API for JavaScript 中的 Autocasting

    Autocasting 是 ArcGIS API for JavaScript 4.x 的一个新特性, 将 json 对象转换成对应的 ArcGIS API f...

    beginor
  • ArcGis API JS 4.X本地化部署与地图的基础使用

    首先下载ArcGIS API for JavaScript4.x,这里下载的是4.19。

    Kiba518
  • 用 JavaScript 截图

    使用 JavaScript 截图,这里我要推荐两款开源组件:一个是 Canvas2Image,它可以将 Canvas 绘图编程 PNG/JPEG/BMP 的图...

    四火
  • html2canvas 一个强大的使用js开发的浏览器网页截图工具

    html2canvas是一个JavaScript类库,它使用了html5和css3的一些新功能特性,实现了在客户端对网页进行截图的功 能。html2canvas...

    王小婷
  • ArcGIS API for JavaScript 4.18基于ES Modules的新开发方式@arcgis/core

    ArcGIS API for JavaScript 4.18中新增加了一种基于ES Modules的新开发方式@arcgis/core,这篇文章就来介绍一下如何...

    X北辰北
  • Javascript 将 HTML 页面生成 PDF 并下载

    最近碰到个需求,需要把当前页面生成 pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

    前端教程
  • Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式

    看到了效果,是不是各位有点小鸡动,是不是也宠宠欲动,有木有?但是具体是怎么实现的呢?下面我来详细的给各位说说我的实现思路吧。

    lzugis
  • Javascript 将 HTML 页面生成 PDF 并下载

    最近碰到个需求,需要把当前页面生成 pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

    前朝楚水
  • ArcGIS JS API 4.16控制地图的缩放大小

    在3.X的ArcGIS JS API版本中我们可以轻松的调用相应的API来实现地图的缩放大小的控制,让实例化后的地图在我们设置的范围中进行缩放,但是在4.X的版...

    X北辰北
  • Javascript将HTML转成PDF并下载「支持多页」

    由于html2canvas只能将它能处理的生成canvas image,因此渲染出来的结果并不是100%与原来一致。但它不需要服务器参与,整个图片都由客户端浏览...

    用户1093975
  • Javascript 将 HTML 页面生成 PDF 并下载

    最近碰到个需求,需要把当前页面生成 pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

    IT派
  • 9个JavaScript图像处理库,收藏好留备用

    1:pica 一个在浏览器中调整图像大小,而不会出现像素失真,处理速度非常快的图片处理库

    王小婷
  • ArcGIS JS API 4.14实现地图加载图片

    主要介绍如何用ArcGIS JS API 4.14实现在二维地图中添加图片的操作。

    X北辰北
  • ArcGIS API for JavaScript应用开发

    ArcGIS API for JavaScript 提供在线版API,4.x 是 ArcGIS API for JavaScript 的新一代版本,实...

    IT技术小咖

扫码关注腾讯云开发者

领取腾讯云代金券