ArcGIS for JS 地图卷帘效果的两种方法
本文适用 ArcGIS JS API 4.28-4.33 版本,核心功能是实现 "地图卷帘效果"------ 通过在同一个地图视图中加载两种不同类型的底图(街道地图和卫星影像),并借助 Swipe
(卷帘)组件,让用户可以拖动分隔线,直观对比两种底图在同一区域的显示差异,常见于地理信息对比分析场景(如城市变迁、地形差异查看等)。
文章目录
- [ArcGIS for JS 地图卷帘效果的两种方法](#ArcGIS for JS 地图卷帘效果的两种方法)
-
- 效果图
- 一、核心功能实现
- 二、方案1:通过Swipe组件方法进行实现(适用4.32以下版本)
-
- [2.1 核心模块与天地图资源加载](#2.1 核心模块与天地图资源加载)
- [2.2 底图图层定义与实例化](#2.2 底图图层定义与实例化)
- [2.3 地图与 2D 视图创建](#2.3 地图与 2D 视图创建)
- [2.4 卷帘微件配置与挂载](#2.4 卷帘微件配置与挂载)
- [2.5 所有代码](#2.5 所有代码)
- 三、方案2:通过Swipe组件方法进行实现(适用4.32以上版本)
-
- [3.1 资源引入](#3.1 资源引入)
- [3.2 地图核心组件配置(HTML 标签式开发)](#3.2 地图核心组件配置(HTML 标签式开发))
- [3.3 ArcGIS 核心模块与天地图工具导入](#3.3 ArcGIS 核心模块与天地图工具导入)
- [3.4 地图实例创建与卷帘功能绑定](#3.4 地图实例创建与卷帘功能绑定)
- [3.5 所有代码](#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 预留了资源。 -
引入
<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>map-components/
组件库(4.33 版本),这是 ArcGIS 推出的 "开箱即用" 地图组件(如<arcgis-map>
、<arcgis-swipe>
),无需手动编写复杂逻辑,直接通过 HTML 标签即可实现地图、卷帘等功能,大幅简化开发流程。
Calcite Components(ArcGIS 官方组件库)
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>