【2025最新】ArcGIS for JS 实现地图卷帘效果
ArcGIS for JS 地图卷帘效果的两种方法
本文适用 ArcGIS JS API 4.28-4.33 版本,核心功能是实现 “地图卷帘效果”—— 通过在同一个地图视图中加载两种不同类型的底图(街道地图和卫星影像),并借助 Swipe
(卷帘)组件,让用户可以拖动分隔线,直观对比两种底图在同一区域的显示差异,常见于地理信息对比分析场景(如城市变迁、地形差异查看等)。
文章目录
- ArcGIS for JS 地图卷帘效果的两种方法
- 效果图
- 一、核心功能实现
- 二、方案1:通过Swipe组件方法进行实现(适用4.32以下版本)
- 2.1 核心模块与天地图资源加载
- 2.2 底图图层定义与实例化
- 2.3 地图与 2D 视图创建
- 2.4 卷帘微件配置与挂载
- 2.5 所有代码
- 三、方案2:通过Swipe组件方法进行实现(适用4.32以上版本)
- 3.1 资源引入
- 3.2 地图核心组件配置(HTML 标签式开发)
- 3.3 ArcGIS 核心模块与天地图工具导入
- 3.4 地图实例创建与卷帘功能绑定
- 3.5 所有代码
工具 /插件/系统 名 | 版本 | 说明 |
---|---|---|
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
:卷帘微件模块,是实现 “卷帘对比” 效果的核心组件。
-
-
通过数组解构赋值,将导入的模块直接赋值给
Map
、MapView
、Swipe
变量,方便后续使用。
-
-
天地图资源加载:
-
从本地
./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 资源引入
新增资源
- 引入
calcite.esm.js
(3.2.1 版本),这是 ArcGIS 提供的轻量级 UI 组件库,包含按钮、列表、弹窗等基础组件,虽代码中未直接使用,但为后续扩展自定义 UI 预留了资源。 - 引入
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>
<!-- 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>
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 layersviewElement.map = new Map({basemap: "satellite",layers: [infrared, nearInfrared],});// wait for swipe to be ready, then add the leading and trailing layersarcgisSwipe.addEventListener("arcgisPropertyChange", (e) => {if (e.detail.name === "state" && arcgisSwipe.state === "ready") {arcgisSwipe.leadingLayers.add(infrared);arcgisSwipe.trailingLayers.add(nearInfrared);}});</script>
</body></html>