【2025最新】ArcGIS for JS 实现地图卷帘效果

ArcGIS for JS 地图卷帘效果的两种方法

本文适用 ArcGIS JS API 4.28-4.33 版本,核心功能是实现 "地图卷帘效果"------ 通过在同一个地图视图中加载两种不同类型的底图(街道地图和卫星影像),并借助 Swipe(卷帘)组件,让用户可以拖动分隔线,直观对比两种底图在同一区域的显示差异,常见于地理信息对比分析场景(如城市变迁、地形差异查看等)。

文章目录

  • [ArcGIS for JS 地图卷帘效果的两种方法](#ArcGIS for JS 地图卷帘效果的两种方法)
工具 /插件/系统 名 版本 说明
ArcGIS JS API 4.28~4.33 地图核心能力(底图加载、视图渲染)
天地图服务 - 提供街道、卫星、地形等底图数据源

效果图

效果图

一、核心功能实现

针对基础引入过程,本文将不过多赘述,有需要的,可自行查阅 《【2025最新】arcgis for js 如何引入前端项目,并使用插件的功能》

二、方案1:通过Swipe组件方法进行实现(适用4.32以下版本)

2.1 核心模块与天地图资源加载

复制代码
const  [Map, MapView, Swipe] = await $arcgis.import( [

   "@arcgis/core/Map",

   "@arcgis/core/views/MapView",

   "@arcgis/core/widgets/Swipe"

]);

import { loadTiandituBasemap } from './js/tiandituLoader.js';

const { config, getUrlTemplate, WebTileLayer, tileInfo } = await loadTiandituBasemap();

这是代码的 "资源初始化阶段",负责加载实现地图功能的核心模块和天地图底图资源:

  • ArcGIS 模块动态导入

    • 使用 $arcgis.import() 方法(ArcGIS API 4.x 推荐的动态导入方式)加载三个关键模块:

      • Map:用于创建地图实例,管理地图的图层集合;

      • MapView:用于创建 2D 地图视图,控制地图的显示位置、缩放级别等交互属性;

      • Swipe:卷帘微件模块,是实现 "卷帘对比" 效果的核心组件。

    • 通过数组解构赋值,将导入的模块直接赋值给 MapMapViewSwipe 变量,方便后续使用。

  • 天地图资源加载

    • 从本地 ./js/tiandituLoader.js 文件中导入 loadTiandituBasemap 函数(该函数是自定义的天地图加载工具,封装了天地图底图的配置逻辑);

    • 调用 loadTiandituBasemap() 并通过解构赋值获取四个关键参数:

      • config:天地图配置信息(如子域名、空间参考等);

      • getUrlTemplate:获取天地图底图 URL 模板的函数(不同类型底图(街道、卫星)对应不同 URL);

      • WebTileLayer:ArcGIS 的 "网络切片图层" 类(天地图底图以切片形式提供,需用该类加载);

      • tileInfo:天地图切片的信息(如切片大小、比例尺等),确保 ArcGIS 能正确解析和渲染天地图切片。

2.2 底图图层定义与实例化

复制代码
// 定义可用于对比的图层

const layers =  [

   {

       id: "streets",

       name: "街道地图",

       type: "街道",

       url: getUrlTemplate('img'),

       opacity: 1

   },

   {

       id: "satellite",

       name: "卫星影像",

       type: "卫星",

       url: getUrlTemplate('ter'),

       opacity: 1

   }

];

// 加载所有图层

const layerInstances = {}, allLayers =  [];

layers.forEach(layerInfo => {

   let layer = new WebTileLayer({

       urlTemplate: layerInfo.url,

       opacity: layerInfo.opacity,

       copyright: "天地图 © 国家地理信息公共服务平台",

       spatialReference: config.spatialReference,

       tileInfo: tileInfo

   });

   if (layer) {

       layerInstances [layerInfo.id] = layer;

       allLayers.push(layer);

   }

});

这部分负责 "定义对比图层" 并 "创建图层实例",是实现卷帘对比的 "数据基础":

  • 图层配置数组

    • layers 数组定义了两个用于对比的底图图层(街道地图和卫星影像),每个图层对象包含五个属性:

      • id:图层唯一标识(后续通过 ID 快速获取图层实例);

      • name:图层名称(用于显示,如图例说明);

      • type:图层类型(标识是街道还是卫星图);

      • url:调用 getUrlTemplate() 生成的底图 URL 模板('img' 对应街道图,'ter' 对应卫星影像,具体映射规则由 tiandituLoader.js 定义);

      • opacity:图层透明度(1 表示完全不透明,确保对比时图层清晰)。

  • 图层实例化与管理

    • 定义 layerInstances(对象,按 ID 存储图层实例,方便快速查找)和 allLayers(数组,存储所有图层实例,用于后续添加到地图);

    • 遍历 layers 数组,为每个图层配置创建 WebTileLayer 实例,传入关键参数:

      • urlTemplate:图层的 URL 模板(从配置中获取);

      • opacity:图层透明度(保持 1,不透明);

      • copyright:版权信息(天地图要求必须标注,符合数据使用规范);

      • spatialReference:空间参考(从 config 中获取,确保与天地图的空间参考一致,避免图层偏移);

      • tileInfo:切片信息(确保 ArcGIS 正确解析切片);

    • 实例化成功后,将图层实例存入 layerInstances(以 id 为键)和 allLayers 数组,完成图层的初始化和管理。

2.3 地图与 2D 视图创建

复制代码
// 创建地图

const map = new Map({

   layers: allLayers,

});

// 创建地图视图

const view = new MapView({

   container: "viewDiv",

   map,

   center:  [104, 35], // 中国中心位置

   zoom: 4

});

这部分是 "地图与视图的核心创建逻辑",将前面准备的图层与页面容器关联,实现地图的基础显示:

  • 地图实例创建

    • 调用 new Map() 创建地图实例 map,并通过 layers: allLayers 将前面实例化的两个底图图层添加到地图中(此时两个图层会叠加显示,但后续卷帘微件会对它们进行分区显示)。
  • 2D 视图创建

    • 调用 new MapView() 创建 2D 地图视图实例 view,传入四个关键参数:

      • container: "viewDiv":指定视图挂载的页面容器(即前面的 #viewDiv),地图会在该容器中渲染;

      • map:关联的地图实例(将视图与地图绑定,视图显示的是该地图的内容);

      • center: [104, 35]:设置地图初始中心点坐标(经度 104°,纬度 35°,大致为中国地理中心位置,确保初始加载时显示中国全貌);

      • zoom: 4:设置初始缩放级别(ArcGIS 的 zoom 值越大,地图显示越详细,4 级对应 "中国全貌" 的显示尺度)。

2.4 卷帘微件配置与挂载

复制代码
// 创建卷帘微件

let swipeWidget = new Swipe({

   view: view,

   leadingLayers:  [layerInstances ["satellite"]],

   trailingLayers:  [layerInstances ["streets"]],

   // position: 50,

   direction: "vertical",

   visibleElements: {

       handle: true,

       divider: true

   }

});

view.ui.add(swipeWidget);

这是实现 "卷帘对比效果" 的最后一步,通过配置和挂载 Swipe 微件,让用户可以交互对比两个底图:

  • 卷帘微件实例化

    • 调用 new Swipe() 创建卷帘微件实例 swipeWidget,传入六个关键配置参数:

      • view: view:关联的地图视图(微件需知道在哪个视图上生效);

      • leadingLayers: [layerInstances["satellite"]]:"领先图层"(卷帘分隔线左侧显示的图层,这里设置为卫星影像);

      • trailingLayers: [layerInstances["streets"]]:"尾随图层"(卷帘分隔线右侧显示的图层,这里设置为街道地图);

      • // position: 50:注释掉的分隔线初始位置配置(值为 0-100 的数字,50 表示初始在视图中间,默认也为中间,故可注释);

      • direction: "vertical":卷帘方向(vertical 表示垂直卷帘,分隔线为竖线,拖动时左右分区;可选 horizontal 水平卷帘,分隔线为横线,上下分区);

      • visibleElements: { handle: true, divider: true }:设置显示的交互元素(handle: true 显示拖动手柄,divider: true 显示分隔线,两者都为 true 确保用户能看到并拖动分隔线)。

  • 微件挂载到视图

    • 调用 view.ui.add(swipeWidget) 将卷帘微件添加到地图视图的 UI 中(ArcGIS 视图的 ui 属性用于管理界面组件,添加后微件会自动在地图上显示,无需手动处理位置)。

2.5 所有代码

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>ArcGIS for JS 地图卷帘效果</title>
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }
    </style>
    <!-- ArcGIS API for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.31/esri/themes/dark/main.css" />
    <script src="https://js.arcgis.com/4.31/"></script>
</head>

<body class="calcite-mode-dark">
    <div id="viewDiv"></div>

    <script type="module">
        const [Map,
            MapView,
            Swipe] = await $arcgis.import([
                "@arcgis/core/Map",
                "@arcgis/core/views/MapView",
                "@arcgis/core/widgets/Swipe"
            ])

        import { loadTiandituBasemap } from './js/tiandituLoader.js';
        const {
            config,
            getUrlTemplate,
            WebTileLayer, tileInfo } = await loadTiandituBasemap();
        // 定义可用于对比的图层
        const layers = [
            {
                id: "streets",
                name: "街道地图",
                type: "街道",
                url: getUrlTemplate('img'),
                opacity: 1
            },
            {
                id: "satellite",
                name: "卫星影像",
                type: "卫星",
                url: getUrlTemplate('ter'),
                opacity: 1
            }
        ];

        // 加载所有图层
        const layerInstances = {}, allLayers = [];
        layers.forEach(layerInfo => {
            let layer = new WebTileLayer({
                urlTemplate: layerInfo.url,
                opacity: layerInfo.opacity,
                // subDomains: config.subDomains,
                copyright: "天地图 © 国家地理信息公共服务平台",
                spatialReference: config.spatialReference,
                tileInfo: tileInfo
            });
            if (layer) {
                layerInstances[layerInfo.id] = layer;
                allLayers.push(layer);
            }
        });
        // 创建地图
        const map = new Map(
            {
                layers: allLayers,
            }
        );
        // 创建地图视图
        const view = new MapView({
            container: "viewDiv",
            map,
            center: [104, 35],
            // 中国中心位置
            zoom: 4
        });

        // 创建卷帘微件
        let swipeWidget = new Swipe({
            view: view,
            leadingLayers: [layerInstances["satellite"]],
            trailingLayers: [layerInstances["streets"]],
            // position: 50,
            direction: "vertical",
            visibleElements: {
                handle: true,
                divider: true
            }
        });
        view.ui.add(swipeWidget);

    </script>
</body>

</html>

三、方案2:通过Swipe组件方法进行实现(适用4.32以上版本)

3.1 资源引入

新增资源

  1. 引入 calcite.esm.js(3.2.1 版本),这是 ArcGIS 提供的轻量级 UI 组件库,包含按钮、列表、弹窗等基础组件,虽代码中未直接使用,但为后续扩展自定义 UI 预留了资源。

  2. 引入 map-components/ 组件库(4.33 版本),这是 ArcGIS 推出的 "开箱即用" 地图组件(如 <arcgis-map><arcgis-swipe>),无需手动编写复杂逻辑,直接通过 HTML 标签即可实现地图、卷帘等功能,大幅简化开发流程。
    Calcite Components(ArcGIS 官方组件库)

    <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script> <link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/dark/main.css" /> <script src="https://js.arcgis.com/4.33/"></script> <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>

3.2 地图核心组件配置(HTML 标签式开发)

复制代码
<arcgis-map zoom="4" center="116.258113, 39.985853">

 <arcgis-zoom position="top-left"></arcgis-zoom>

 <arcgis-swipe swipe-position="32"></arcgis-swipe>

 <arcgis-expand position="top-right">

   <arcgis-layer-list></arcgis-layer-list>

 </arcgis-expand>

</arcgis-map>

这部分是代码的 "视觉与交互核心",通过 ArcGIS 地图组件直接定义地图的基础属性与功能模块,属于 "低代码" 开发模式:

  • **核心地图容器 **<arcgis-map>

    • 这是 ArcGIS 地图组件的 "根容器",所有地图相关功能(缩放、卷帘、图层列表)都需嵌套在该标签内;

    • 关键属性:

      • zoom="15":设置地图初始缩放级别(ArcGIS 缩放级别数值越大,地图显示越详细,15 级对应 "城市街区" 级别的显示精度);

      • center="-154.88, 19.46":设置地图初始中心点坐标(经度 -154.88°,纬度 19.46°,对应美国夏威夷州火奴鲁鲁附近区域)。

  • **缩放控制组件 **<arcgis-zoom>

    • 功能:提供 "放大""缩小" 两个按钮,支持用户手动调整地图缩放级别;

    • position="top-left":设置组件在地图容器中的位置(左上角),符合用户 "左上角控制缩放" 的使用习惯。

  • **卷帘对比组件 **<arcgis-swipe>

    • 功能:实现 "卷帘效果",通过拖动分隔线,对比分隔线两侧不同图层的显示差异;

    • swipe-position="32":设置卷帘分隔线的初始位置(数值范围 0-100,单位为百分比,32 表示分隔线初始在地图容器水平方向 32% 的位置,左侧显示 "领先图层",右侧显示 "尾随图层")。

  • 图层列表组件(带折叠功能)

    • <arcgis-expand>:折叠容器组件,点击可展开 / 收起内部内容,避免图层列表占用过多地图空间;

      • position="top-right":设置折叠容器在地图右上角;
    • <arcgis-layer-list>:图层列表组件,自动显示地图中所有已加载的图层,支持用户手动控制图层的显示 / 隐藏(如勾选 / 取消勾选图层),方便管理多图层。

3.3 ArcGIS 核心模块与天地图工具导入

复制代码
const [Map, TileLayer] = await \$arcgis.import([

 "@arcgis/core/Map.js",

 "@arcgis/core/layers/TileLayer.js",

]);

const arcgisSwipe = document.querySelector("arcgis-swipe");

const viewElement = document.querySelector("arcgis-map");

import { loadTiandituBasemap } from './js/tiandituLoader.js';

const {

 config,

 getUrlTemplate,

 WebTileLayer, tileInfo } = await loadTiandituBasemap();
 
    const arcgisSwipe = document.querySelector("arcgis-swipe");
    const viewElement = document.querySelector("arcgis-map");
    import { loadTiandituBasemap } from './js/tiandituLoader.js';
    const {
      config,
      getUrlTemplate,
      WebTileLayer, tileInfo } = await loadTiandituBasemap();

    const infrared = new WebTileLayer({
      urlTemplate:getUrlTemplate('img'),
      copyright: "天地图 © 国家地理信息公共服务平台",
      spatialReference: config.spatialReference,
      tileInfo: tileInfo
    });
    const nearInfrared = new WebTileLayer({
      urlTemplate: getUrlTemplate('ter'),
      copyright: "天地图 © 国家地理信息公共服务平台",
      spatialReference: config.spatialReference,
      tileInfo: tileInfo
    });

这部分是 "数据与工具准备阶段",负责导入实现图层加载与卷帘功能的核心模块,

与方法1相同,需要引入ArcGIS 核心模块、天地图底图图层创建,此处不再赘述;

3.4 地图实例创建与卷帘功能绑定

复制代码
// 创建地图实例并关联到地图容器

viewElement.map = new Map({

 basemap: "satellite",

 layers: [infrared, nearInfrared],

});

// 监听卷帘组件状态,绑定对比图层

arcgisSwipe.addEventListener("arcgisPropertyChange", (e) => {

 if (e.detail.name === "state" && arcgisSwipe.state === "ready") {

   arcgisSwipe.leadingLayers.add(infrared);

   arcgisSwipe.trailingLayers.add(nearInfrared);

 }

});

这部分是 "功能联动核心",将创建的地图实例、图层与前面配置的 HTML 组件绑定,实现完整的卷帘效果:

  • 卷帘组件与图层绑定

    • 监听卷帘组件 <arcgis-swipe>arcgisPropertyChange 事件(ArcGIS 组件特有的 "属性变化事件",当组件状态、位置等属性变化时触发);

    • 事件回调逻辑:

      • e.detail.name === "state":判断变化的属性是 "组件状态(state)";

      • arcgisSwipe.state === "ready":确保卷帘组件已初始化完成(状态为 "就绪",避免在组件未加载完成时绑定图层导致报错);

      • arcgisSwipe.leadingLayers.add(infrared):将天地图影像图(infrared)添加到卷帘的 "领先图层"(分隔线左侧显示的图层);

      • arcgisSwipe.trailingLayers.add(nearInfrared):将天地图地形图(nearInfrared)添加到卷帘的 "尾随图层"(分隔线右侧显示的图层);

    • 至此,卷帘组件已能控制两个图层的显示区域,用户拖动分隔线即可对比影像图与地形图的差异。

3.5 所有代码

复制代码
<!DOCTYPE html>
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <title>ArcGIS for JS 地图卷帘效果</title>

  <style>
    html,
    body {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <!-- Load Calcite components from CDN -->
  <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script>

  <!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
  <link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/dark/main.css" />
  <script src="https://js.arcgis.com/4.33/"></script>

  <!-- Load Map components from CDN-->
  <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>
</head>

<body class="calcite-mode-dark">
  <arcgis-map zoom="4" center="116.258113, 39.985853">
    <arcgis-zoom position="top-left"></arcgis-zoom>
    <arcgis-swipe swipe-position="32"></arcgis-swipe>
    <arcgis-expand position="top-right">
      <arcgis-layer-list></arcgis-layer-list>
    </arcgis-expand>
  </arcgis-map>

  <script type="module">
    const [Map, TileLayer] = await $arcgis.import([
      "@arcgis/core/Map.js",
      "@arcgis/core/layers/TileLayer.js",
    ]);

    const arcgisSwipe = document.querySelector("arcgis-swipe");
    const viewElement = document.querySelector("arcgis-map");
    import { loadTiandituBasemap } from './js/tiandituLoader.js';
    const {
      config,
      getUrlTemplate,
      WebTileLayer, tileInfo } = await loadTiandituBasemap();

    const infrared = new WebTileLayer({
      urlTemplate:getUrlTemplate('img'),
      copyright: "天地图 © 国家地理信息公共服务平台",
      spatialReference: config.spatialReference,
      tileInfo: tileInfo
    });
    const nearInfrared = new WebTileLayer({
      urlTemplate: getUrlTemplate('ter'),
      copyright: "天地图 © 国家地理信息公共服务平台",
      spatialReference: config.spatialReference,
      tileInfo: tileInfo
    });
    // create the map with layers
    viewElement.map = new Map({
      basemap: "satellite",
      layers: [infrared, nearInfrared],
    });
    // wait for swipe to be ready, then add the leading and trailing layers
    arcgisSwipe.addEventListener("arcgisPropertyChange", (e) => {
      if (e.detail.name === "state" && arcgisSwipe.state === "ready") {
        arcgisSwipe.leadingLayers.add(infrared);
        arcgisSwipe.trailingLayers.add(nearInfrared);
      }
    });
  </script>
</body>

</html>
相关推荐
liulilittle3 小时前
网络编程基础算法剖析:从字节序转换到CIDR掩码计算
开发语言·网络·c++·算法·通信
代码村新手3 小时前
C语言-字符函数和字符串函数
c语言·开发语言
数据知道4 小时前
Go基础:Go语言应用的各种部署
开发语言·后端·golang·go语言
刀客1234 小时前
C++ 面试总结
开发语言·c++·面试
hqwest4 小时前
QT肝8天16--加载动态菜单
开发语言·数据库·qt·ui·sqlite
友友马4 小时前
『 QT 』Qt初识
开发语言·qt
listhi5204 小时前
基于MATLAB的高斯混合模型(GMM)实现
开发语言·matlab
兰亭妙微4 小时前
兰亭妙微QT软件开发经验:跨平台桌面端界面设计的三大要点
开发语言·qt
Mingze03145 小时前
考研408之栈与队列学习
开发语言·c++·学习·考研·算法