详细介绍:【2025最新】ArcGIS for JS 实现地图卷帘效果

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

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

工具 /插件/系统 名版本说明
ArcGIS JS API4.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 所有代码




    
    
    ArcGIS for JS 地图卷帘效果
    
    
    
    <script src="https://js.arcgis.com/4.31/"></script>


    
<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>

三、方案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>


<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 地图组件直接定义地图的基础属性与功能模块,属于 “低代码” 开发模式:

  • **核心地图容器 **<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 所有代码





  
  
  ArcGIS for JS 地图卷帘效果
  
  
  <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script>
  
  
  <script src="https://js.arcgis.com/4.33/"></script>
  
  <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>


  
    
    
    
      
    
  
  <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>

posted on 2025-10-31 15:02  wgwyanfs  阅读(55)  评论(0)    收藏  举报

导航