10-ArcGIS For JavaScript -- 三维场景导出为图片(PNG/JPEG)

发布于:2025-03-27 ⋅ 阅读:(29) ⋅ 点赞:(0)


前言

三维场景开发过程中,时常会出现需要保存当前场景的内容,去做一些分析或者场景展示。如果使用计算机自带的截图功能,只能将当前的页面截取到计算机本地,不能直接提供给程序去使用。
ArcGIS For JavaScript 的SceneView对象中提供了一个takeScreenshot函数,它可以创建当前视图的屏幕截图。屏幕截图仅包括在画布上渲染的元素 (所有地理元素),但不包括覆盖的 DOM 元素 (UI、弹出窗口等)。默认情况下,会创建整个视图的屏幕截图。不同的选项允许创建不同类型的屏幕截图,包括以不同的纵横比、不同的分辨率进行屏幕截图和创建缩略图。相关参数可以参考官网
在这里插入图片描述

一、takeScreenshot参数

  • format:默认值为png,生成的编码数据 url 的格式。可能值:“jpg”|“png”。
  • quality:默认值为98,格式为 jpg 时编码图像的质量 (0 - 100)。
  • width:屏幕截图的宽度 (默认为区域宽度)。如果未指定,高度将根据屏幕截图区域的纵横比自动得出。
  • height:屏幕截图的高度 (默认为区域高度)。如果未指定,宽度将根据屏幕截图区域的纵横比自动得出。
  • area:指定是否截取视图特定区域的屏幕截图。区域坐标相对于内边距视图的原点 (请参阅 padding),并将裁剪为视图大小。默认为整个视图 (不包括内边距)。
  • ignorePadding:指示是否应忽略视图内边距。将此属性设置为 true 以允许在屏幕截图中包含内边距区域。

二、示例

1、以当前视图相同的分辨率进行截图

view.takeScreenshot().then(function(screenshot) {
  let imageElement = document.getElementById("screenshotImage");
  imageElement.src = screenshot.dataUrl;
});

2、从当前视图创建一个方形缩略图

let options = {
  width: 200,
  height: 200
};

view.takeScreenshot(options).then(function(screenshot) {
  let imageElement = document.getElementById("screenshotImage");
  imageElement.src = screenshot.dataUrl;
});

3、取一个高分辨率的方形截图

let options = {
  width: 2048,
  height: 2048
};

view.takeScreenshot(options).then(function(screenshot) {
  let imageElement = document.getElementById("screenshotImage");
  imageElement.src = screenshot.dataUrl;
});

3、在视图中心截取一个小区域的屏幕截图

// 计算视图的大小,不包括padding的部分
let padding = view.padding;
let innerWidth = view.width - padding.left - padding.right;
let innerHeight = view.height - padding.top - padding.bottom;

// 期望的区域大小
let width = 200;
let height = 200;

let options = {
  area: {
    x: (innerWidth - width) / 2,
    y: (innerHeight - height) / 2,
    width: width,
    height: height
  }
};

view.takeScreenshot(options).then(function(screenshot) {
  let imageElement = document.getElementById("screenshotImage");
  imageElement.src = screenshot.dataUrl;
});

三、完成代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://js.arcgis.com/4.30/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.30/"></script>
    <style>
        html,
        body,
        #viewDiv {
            height: 100%;
            width: 100%;
            margin: 0;
            padding: 0;
        }

        .screenshotDiv {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            text-align: center;
            background-color: rgba(255, 255, 255, 0.8);
        }

        .screenshotDiv img {
            border: 10px solid white;
            box-shadow: 2px 2px 5px 0 rgba(0, 0, 0, 0.5);
        }

        .screenshotDiv>* {
            margin: 0.5em;
        }

        .actionButton {
            padding: 0.4em;
            color: black;
            border: 1px solid #5c5e5e;
            text-align: center;
            background-color: #f1efef;
            cursor: pointer;
            width: 100px;
            border-radius: 3px;
            font-size: 16px;
        }

        .actionButton:hover,
        .actionButton:focus {
            background: #0079c1;
            color: white;
        }

        .closeBtn {
            display: inherit;
            margin: auto;
        }

        .title {
            position: absolute;
            top: 20px;
            width: 600px;
            left: calc(50% - 300px);
            background: #ffffff99;
            padding: 5px;
            border-radius: 3px;
        }
        .hide{
            display: none;
        }
    </style>
    <script>
        require([
            'esri/geometry/Point',
            "esri/geometry/SpatialReference",
            "esri/geometry/Mesh",
            "esri/views/SceneView",
            "esri/Map",
            "esri/Graphic",
            "esri/symbols/FillSymbol3DLayer",
            "esri/symbols/MeshSymbol3D",
            "esri/geometry/support/MeshMaterial",
            "esri/geometry/support/MeshLocalVertexSpace",
            "esri/layers/IntegratedMeshLayer"
        ], (Point, SpatialReference, Mesh, SceneView, Map,
            Graphic, FillSymbol3DLayer, MeshSymbol3D, MeshMaterial, MeshLocalVertexSpace, IntegratedMeshLayer) => {

            let layer = new IntegratedMeshLayer({
                url: "https://tiles.arcgis.com/tiles/cFEFS0EWrhfDeVw9/arcgis/rest/services/Utrecht_Buildings_2021/SceneServer"
            })
            let map = new Map({
                layers: [layer],
                basemap: 'satellite'
            })
            let center = [116.4074, 39.9042, 300];

            let view = new SceneView({
                container: 'viewDiv',
                map,
            })

            view.when(function () {
                view.extent = layer.fullExtent;
            })

            const screenshotBtn = document.getElementById("screenshotBtn");
            // const maskDiv = document.getElementById("maskDiv");
            let screenshotDiv = document.getElementById("screenshotDiv");
            const closeBtn = document.getElementById("closeBtn");
            screenshotDiv.classList.add("hide");
            view.ui.empty("top-right");
            view.ui.add(screenshotBtn, "top-right");
            let that = this;

            screenshotBtn.addEventListener("click", () => {
                let self = that;
                let area = null;
                area = {
                    x: view.canvas.clientLeft,
                    y: view.canvas.clientTop,
                    height: view.height,
                    width: view.width
                }

                //"jpg"|"png"
                view.takeScreenshot({ area: area, format: "png" })
                    .then((screenshot) => {
                        console.log(screenshot);//screenshot.dataUrl为base64信息
                        // let blob = self.base64ToBlob(screenshot.dataUrl, 'png')
                        // self.download("test.png", blob);
                        downloadFile(screenshot.dataUrl, 'download', '.png');
                        showPreview(screenshot);//可以删除,只是为了展示截图信息
                    })
            });

            /**
             * desc: base64对象转blob文件对象
             * @param urlData  :数据的base64对象
             * @param type  :类型 png,pdf,doc,mp3等;
             * @returns {Blob}:Blob文件对象
             */
            function base64ToBlob(urlData, type) {
                let arr = urlData.split(',');
                let array = arr[0].match(/:(.*?);/);
                let mime = (array && array.length > 1 ? array[1] : type) || type;
                // 去掉url的头,并转化为byte
                let bytes = window.atob(arr[2]);
                // 处理异常,将ascii码小于0的转换为大于0
                let ab = new ArrayBuffer(bytes.length);
                // 生成视图(直接针对内存):8位无符号整数,长度1个字节
                let ia = new Uint8Array(ab);
                for (let i = 0; i < bytes.length; i++) {
                    ia[i] = bytes.charCodeAt(i);
                }
                return new Blob([ab], {
                    type: mime
                });
            }

            /**
           * desc: 下载导出文件
           * @param blob  :返回数据的blob对象或链接
           * @param fileName  :下载后文件名标记
           * @param fileType  :文件类 word(docx) excel(xlsx) ppt等
           */
           function downloadExportFile(blob, fileName, fileType) {
                let downloadElement = document.createElement('a');
                let href = blob;
                if (typeof blob == 'string') {
                    downloadElement.target = '_blank';
                } else {
                    href = window.URL.createObjectURL(blob); //创建下载的链接
                }
                downloadElement.href = href;
                downloadElement.download = fileName + '.' + fileType; //下载后文件名
                document.body.appendChild(downloadElement);
                downloadElement.click(); //触发点击下载
                document.body.removeChild(downloadElement); //下载完成移除元素
                if (typeof blob != 'string') {
                    window.URL.revokeObjectURL(href); //释放掉blob对象
                }
            }

            /**
             * desc: base64转文件并下载
             * @param base64 {String} : base64数据
             * @param fileType {String} : 要导出的文件类型png,pdf,doc,mp3等
             * @param fileName {String} : 文件名
             */
             function downloadFile(base64, fileName, fileType) {
                let typeHeader = 'data:application/' + fileType + ';base64,' // 定义base64 头部文件类型
                let converedBase64 = typeHeader + base64;  // 拼接最终的base64
                let blob = base64ToBlob(converedBase64, fileType)  // 转成blob对象
                downloadExportFile(blob, fileName, fileType) // 下载文件
            }

            function showPreview(screenshot) {
                screenshotDiv.classList.remove("hide");
                // add the screenshot dataUrl as the src of an image element
                const screenshotImage = document.getElementsByClassName(
                    "js-screenshot-image"
                )[0];
                screenshotImage.width = screenshot.data.width * 0.3;
                screenshotImage.height = screenshot.data.height * 0.3;
                screenshotImage.src = screenshot.dataUrl;
            }

            document.getElementById('closeBtn').addEventListener('click', function () {
                screenshotDiv.classList.add("hide");
            })

        })
    </script>
</head>

<body>
    <div id="viewDiv">
        <button id="screenshotBtn" class="esri-widget actionButton" aria-label="Select screenshot area"
            title="Select screenshot area">
            截图
        </button>

        <div id="screenshotDiv" class="screenshotDiv">
            <img class="js-screenshot-image" />
            <button id="closeBtn" class="closeBtn action-button" aria-label="Back to webscene" title="Back to webscene">
                返回到web场景
            </button>
        </div>
    </div>
</body>

</html>

四、结果图

在这里插入图片描述

在这里插入图片描述