【2025最新】ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版) - 教程

ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版)

本文适用 ArcGIS JS API 4.28-4.33 版本,核心是实现一款进阶版地图卷帘组件。该组件在基础卷帘功能上,新增并强化了三大关键特性:多图层切换自定义滑块样式动态光影效果,并支持对 Swipe 组件参数的动态修改。
本文为

工具 /插件/系统 名版本说明
ArcGIS JS API4.28~4.33地图核心能力(底图加载、视图渲染)
天地图服务-提供街道、卫星、地形等底图数据源

效果图

效果图
在这里插入图片描述

功能展示

  • 垂直/水平方向的卷帘切换
  • 自定义卷帘位置调整
  • 可选择对比的图层
  • 键盘方向键控制
  • 重置和全图查看功能
  • 美观的UI设计和动画效果

实现步骤

1. 基础HTML结构搭建

首先,我们需要创建基本的HTML页面结构,包括地图容器、控制面板和提示信息:

<div id="viewDiv"></div>
  <div id="controlPanel">
  <!-- 控制面板内容 -->
  </div>
    <div class="instruction">
  <i class="fa fa-hand-pointer-o" aria-hidden="true"></i> 拖动卷帘滑块或使用键盘方向键调整位置
  </div>
  • viewDiv:用于显示地图的容器
  • controlPanel:包含各种控制元素的面板
  • instruction:底部的操作提示

2. 样式设计

为了使界面美观且易用,我们需要设计相应的CSS样式:

/* 基础样式 */
html, body, #viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
/* 控制面板样式 */
#controlPanel {
position: absolute;
top: 10px;
right: 10px;
background-color: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 100;
width: 280px;
}
/* 自定义卷帘样式 */
.esri-swipe__handle {
background-color: #0079c1 !important;
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) !important;
}
/* 动态光影效果 */
@keyframes glow {
0% { box-shadow: 0 0 5px rgba(0, 121, 193, 0.5); }
50% { box-shadow: 0 0 20px rgba(0, 121, 193, 0.8); }
100% { box-shadow: 0 0 5px rgba(0, 121, 193, 0.5); }
}
.esri-swipe__divider {
animation: glow 2s infinite;
}

特别注意对卷帘组件的样式定制,通过!important覆盖默认样式,并添加了光影动画效果增强视觉体验。

3. 引入必要的库

我们需要引入ArcGIS API for JavaScript和Font Awesome图标库:

<!-- ArcGIS API for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css">
  <script src="https://js.arcgis.com/4.28/"></script>
    <!-- Font Awesome -->
        <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

4. 初始化地图和图层

首先,我们需要导入ArcGIS API的核心模块并创建地图实例:

const [Map, MapView, Swipe] = await $arcgis.import([
"@arcgis/core/Map",
"@arcgis/core/views/MapView",
"@arcgis/core/widgets/Swipe"
]);
// 创建地图
const map = new Map();
// 创建地图视图
const view = new MapView({
container: "viewDiv",
map: map,
center: [104, 35], // 中国中心位置
zoom: 4
});

接下来,定义并加载需要对比的图层:

// 定义可用于对比的图层
const layers = [
{
id: "streets",
name: "街道地图",
type: "tile",
url: getUrlTemplate('vec'),
opacity: 1
},
{
id: "satellite",
name: "卫星影像",
type: "tile",
url: getUrlTemplate('img'),
opacity: 1
},
{
id: "topo",
name: "地形图",
type: "tile",
url: getUrlTemplate('cta'),
opacity: 1
}
];
// 加载所有图层
const layerInstances = {};
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;
map.add(layer);
}
});

5. 创建图层选择器

为了允许用户切换对比的图层,我们需要创建下拉选择器并填充图层选项:

// 填充图层选择器
const leadingLayerSelect = document.getElementById("leadingLayerSelect");
const trailingLayerSelect = document.getElementById("trailingLayerSelect");
layers.forEach(layerInfo => {
// 为右侧图层选择器添加选项
const leadingOption = document.createElement("option");
leadingOption.value = layerInfo.id;
leadingOption.textContent = layerInfo.name;
leadingLayerSelect.appendChild(leadingOption);
// 为左侧图层选择器添加选项
const trailingOption = document.createElement("option");
trailingOption.value = layerInfo.id;
trailingOption.textContent = layerInfo.name;
trailingLayerSelect.appendChild(trailingOption);
});
// 设置默认选择的图层
leadingLayerSelect.value = "streets";
trailingLayerSelect.value = "satellite";

6. 初始化卷帘组件

创建Swipe组件并添加到地图视图中:

let swipeWidget = new Swipe({
view: view,
leadingLayers: [layerInstances["streets"]],
trailingLayers: [layerInstances["satellite"]],
position: 50, // 初始位置在50%处
direction: "vertical", // 初始为垂直方向
visibleElements: {
handle: true,
divider: true
}
});
// 将卷帘组件添加到视图
view.ui.add(swipeWidget);

7. 添加交互功能

为了使卷帘工具更加易用,我们需要添加各种交互功能:

卷帘位置调整
const swipePositionSlider = document.getElementById("swipePosition");
const positionValue = document.getElementById("positionValue");
swipePositionSlider.addEventListener("input", function () {
const position = parseInt(this.value);
swipeWidget.position = position;
positionValue.textContent = position + "%";
});
卷帘方向切换
const verticalBtn = document.getElementById("verticalBtn");
const horizontalBtn = document.getElementById("horizontalBtn");
verticalBtn.addEventListener("click", function () {
swipeWidget.direction = "vertical";
verticalBtn.classList.add("active");
horizontalBtn.classList.remove("active");
});
horizontalBtn.addEventListener("click", function () {
swipeWidget.direction = "horizontal";
horizontalBtn.classList.add("active");
verticalBtn.classList.remove("active");
});
图层切换功能
leadingLayerSelect.addEventListener("change", function () {
const selectedLayerId = this.value;
const selectedLayer = layerInstances[selectedLayerId];
if (selectedLayer) {
swipeWidget.leadingLayers.removeAll();
swipeWidget.leadingLayers.add(selectedLayer);
}
});
trailingLayerSelect.addEventListener("change", function () {
const selectedLayerId = this.value;
const selectedLayer = layerInstances[selectedLayerId];
if (selectedLayer) {
swipeWidget.trailingLayers.removeAll();
swipeWidget.trailingLayers.add(selectedLayer);
}
});
键盘控制
document.addEventListener("keydown", function (event) {
if (event.key === "ArrowLeft" || event.key === "ArrowRight" ||
event.key === "ArrowUp" || event.key === "ArrowDown") {
event.preventDefault();
let newPosition = swipeWidget.position;
// 根据方向键和当前卷帘方向调整位置
if (swipeWidget.direction === "vertical") {
if (event.key === "ArrowLeft") {
newPosition = Math.max(0, newPosition - 1);
} else if (event.key === "ArrowRight") {
newPosition = Math.min(100, newPosition + 1);
}
} else {
if (event.key === "ArrowUp") {
newPosition = Math.max(0, newPosition - 1);
} else if (event.key === "ArrowDown") {
newPosition = Math.min(100, newPosition + 1);
}
}
swipeWidget.position = newPosition;
// 更新滑块和显示值
swipePositionSlider.value = newPosition;
positionValue.textContent = newPosition + "%";
}
});
重置和全图功能
// 重置按钮事件
document.getElementById("resetBtn").addEventListener("click", function () {
// 重置卷帘位置
swipeWidget.position = 50;
swipePositionSlider.value = 50;
positionValue.textContent = "50%";
// 重置图层选择
leadingLayerSelect.value = "streets";
trailingLayerSelect.value = "satellite";
// 重置卷帘方向
swipeWidget.direction = "vertical";
verticalBtn.classList.add("active");
horizontalBtn.classList.remove("active");
});
// 全图按钮事件
document.getElementById("fullExtentBtn").addEventListener("click", function () {
view.goTo({
center: [104, 35],
zoom: 4
});
});

所有代码

index.html




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


    
卷帘位置
50%
卷帘方向
右侧图层
左侧图层
拖动卷帘滑块或使用键盘方向键调整位置
<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: "tile", url: getUrlTemplate('vec'), opacity: 1 }, { id: "satellite", name: "卫星影像", type: "tile", url: getUrlTemplate('img'), opacity: 1 }, { id: "topo", name: "地形图", type: "tile", url: getUrlTemplate('cta'), opacity: 1 } ]; // 创建地图 const map = new Map(); // 创建地图视图 const view = new MapView({ container: "viewDiv", map: map, center: [104, 35], // 中国中心位置 zoom: 4 }); // 加载所有图层 const layerInstances = {}; 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; map.add(layer); } }); // 填充图层选择器 const leadingLayerSelect = document.getElementById("leadingLayerSelect"); const trailingLayerSelect = document.getElementById("trailingLayerSelect"); layers.forEach(layerInfo => { const leadingOption = document.createElement("option"); leadingOption.value = layerInfo.id; leadingOption.textContent = layerInfo.name; leadingLayerSelect.appendChild(leadingOption); const trailingOption = document.createElement("option"); trailingOption.value = layerInfo.id; trailingOption.textContent = layerInfo.name; trailingLayerSelect.appendChild(trailingOption); }); // 默认选择的图层 leadingLayerSelect.value = "streets"; trailingLayerSelect.value = "satellite"; // 创建卷帘微件 let swipeWidget = new Swipe({ view: view, leadingLayers: [layerInstances["streets"]], trailingLayers: [layerInstances["satellite"]], position: 50, direction: "vertical", visibleElements: { handle: true, divider: true } }); view.ui.add(swipeWidget); // 更新图层选择器事件 leadingLayerSelect.addEventListener("change", function () { const selectedLayerId = this.value; const selectedLayer = layerInstances[selectedLayerId]; if (selectedLayer) { // 移除当前的leading图层 swipeWidget.leadingLayers.removeAll(); // 添加新的leading图层 swipeWidget.leadingLayers.add(selectedLayer); } }); trailingLayerSelect.addEventListener("change", function () { const selectedLayerId = this.value; const selectedLayer = layerInstances[selectedLayerId]; if (selectedLayer) { // 移除当前的trailing图层 swipeWidget.trailingLayers.removeAll(); // 添加新的trailing图层 swipeWidget.trailingLayers.add(selectedLayer); // 更新透明度滑块 document.getElementById("trailingOpacity").value = selectedLayer.opacity * 100; document.getElementById("trailingOpacityValue").textContent = (selectedLayer.opacity * 100) + "%"; } }); // 卷帘位置滑块事件 const swipePositionSlider = document.getElementById("swipePosition"); const positionValue = document.getElementById("positionValue"); swipePositionSlider.addEventListener("input", function () { const position = parseInt(this.value); swipeWidget.position = position; positionValue.textContent = position + "%"; }); // 卷帘方向按钮事件 const verticalBtn = document.getElementById("verticalBtn"); const horizontalBtn = document.getElementById("horizontalBtn"); verticalBtn.addEventListener("click", function () { swipeWidget.direction = "vertical"; verticalBtn.classList.add("active"); horizontalBtn.classList.remove("active"); }); horizontalBtn.addEventListener("click", function () { swipeWidget.direction = "horizontal"; horizontalBtn.classList.add("active"); verticalBtn.classList.remove("active"); }); // 重置按钮事件 document.getElementById("resetBtn").addEventListener("click", function () { // 重置卷帘位置 swipeWidget.position = 50; swipePositionSlider.value = 50; positionValue.textContent = "50%"; // 重置透明度 const leadingLayer = swipeWidget.leadingLayers.getItemAt(0); const trailingLayer = swipeWidget.trailingLayers.getItemAt(0); if (leadingLayer) { leadingLayer.opacity = 1; document.getElementById("leadingOpacity").value = 100; document.getElementById("leadingOpacityValue").textContent = "100%"; } if (trailingLayer) { trailingLayer.opacity = 1; document.getElementById("trailingOpacity").value = 100; document.getElementById("trailingOpacityValue").textContent = "100%"; } // 重置图层选择 leadingLayerSelect.value = "streets"; trailingLayerSelect.value = "satellite"; // 重置卷帘方向 swipeWidget.direction = "vertical"; verticalBtn.classList.add("active"); horizontalBtn.classList.remove("active"); }); // 全图按钮事件 document.getElementById("fullExtentBtn").addEventListener("click", function () { view.goTo({ center: [104, 35], zoom: 4 }); }); // 键盘方向键控制卷帘位置 document.addEventListener("keydown", function (event) { if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") { event.preventDefault(); let newPosition = swipeWidget.position; // 根据方向键和当前卷帘方向调整位置 if (swipeWidget.direction === "vertical") { if (event.key === "ArrowLeft") { newPosition = Math.max(0, newPosition - 1); } else if (event.key === "ArrowRight") { newPosition = Math.min(100, newPosition + 1); } } else { if (event.key === "ArrowUp") { newPosition = Math.max(0, newPosition - 1); } else if (event.key === "ArrowDown") { newPosition = Math.min(100, newPosition + 1); } } swipeWidget.position = newPosition; } }); // 初始化透明度滑块值 document.getElementById("leadingOpacity").value = 100; document.getElementById("leadingOpacityValue").textContent = "100%"; document.getElementById("trailingOpacity").value = 100; document.getElementById("trailingOpacityValue").textContent = "100%"; // 添加按钮激活样式 verticalBtn.classList.add("active"); </script>

tiandituLoader.js

/**
 * 天地图加载公共模块
 * 功能:封装天地图底图加载逻辑,返回配置好的Basemap实例
 * 依赖:ArcGIS API 4.x
 */
export async function loadTiandituBasemap() {
    try {
        // 1. 按需导入ArcGIS核心模块
        const [
            WebTileLayer,
            Basemap,
            TileInfo
        ] = await $arcgis.import([
            "@arcgis/core/layers/WebTileLayer",
            "@arcgis/core/Basemap",
            "@arcgis/core/layers/support/TileInfo",
        ]);
        // 2. 配置参数(可根据需求调整)
        const config = {
            tk:"你的密钥", // 天地图密钥
            spatialReference: { wkid: 4326 },       // 目标坐标系(WGS84)
            subDomains: ["0", "1", "2", "3", "4", "5", "6", "7"], // 多子域名
            tileMatrixSet: "c",                     // 天地图瓦片矩阵集
            layerType: {
                vec: "vec", // 矢量底图
                cva: "cva"  // 矢量注记
            }
        };
        // 3. 定义瓦片信息(匹配WGS84坐标系的瓦片规则)
        const tileInfo = new TileInfo({
            dpi: 90.71428571427429,
            rows: 256,
            cols: 256,
            compressionQuality: 0,
            origin: { x: -180, y: 90 },
            spatialReference: config.spatialReference,
            lods: [
                { level: 2, levelValue: 2, resolution: 0.3515625, scale: 147748796.52937502 },
                { level: 3, levelValue: 3, resolution: 0.17578125, scale: 73874398.264687508 },
                { level: 4, levelValue: 4, resolution: 0.087890625, scale: 36937199.132343754 },
                { level: 5, levelValue: 5, resolution: 0.0439453125, scale: 18468599.566171877 },
                { level: 6, levelValue: 6, resolution: 0.02197265625, scale: 9234299.7830859385 },
                { level: 7, levelValue: 7, resolution: 0.010986328125, scale: 4617149.8915429693 },
                { level: 8, levelValue: 8, resolution: 0.0054931640625, scale: 2308574.9457714846 },
                { level: 9, levelValue: 9, resolution: 0.00274658203125, scale: 1154287.4728857423 },
                { level: 10, levelValue: 10, resolution: 0.001373291015625, scale: 577143.73644287116 },
                { level: 11, levelValue: 11, resolution: 0.0006866455078125, scale: 288571.86822143558 },
                { level: 12, levelValue: 12, resolution: 0.00034332275390625, scale: 144285.93411071779 },
                { level: 13, levelValue: 13, resolution: 0.000171661376953125, scale: 72142.967055358895 },
                { level: 14, levelValue: 14, resolution: 8.58306884765625e-005, scale: 36071.483527679447 },
                { level: 15, levelValue: 15, resolution: 4.291534423828125e-005, scale: 18035.741763839724 },
                { level: 16, levelValue: 16, resolution: 2.1457672119140625e-005, scale: 9017.8708819198619 },
                { level: 17, levelValue: 17, resolution: 1.0728836059570313e-005, scale: 4508.9354409599309 },
                { level: 18, levelValue: 18, resolution: 5.3644180297851563e-006, scale: 2254.4677204799655 },
                { level: 19, levelValue: 19, resolution: 2.68220901489257815e-006, scale: 1127.23386023998275 },
                { level: 20, levelValue: 20, resolution: 1.341104507446289075e-006, scale: 563.616930119991375 }
            ]
        });
        // 4. 构建天地图URL模板(支持多子域名)
        const getUrlTemplate = (layer) => {
            return `http://t0.tianditu.gov.cn/${layer}_${config.tileMatrixSet}/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${layer}&STYLE=default&TILEMATRIXSET=${config.tileMatrixSet}&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}&FORMAT=tiles&tk=${config.tk}`;
        };
        // 5. 创建矢量底图图层
        const vecLayer = new WebTileLayer({
            urlTemplate: getUrlTemplate(config.layerType.vec),
            subDomains: config.subDomains,
            copyright: "天地图 © 国家地理信息公共服务平台",
            spatialReference: config.spatialReference,
            tileInfo: tileInfo
        });
        // 6. 创建矢量注记图层
        const cvaLayer = new WebTileLayer({
            urlTemplate: getUrlTemplate(config.layerType.cva),
            subDomains: config.subDomains,
            copyright: "天地图 © 国家地理信息公共服务平台",
            spatialReference: config.spatialReference,
            tileInfo: tileInfo
        });
        // 7. 创建自定义底图并返回
        const tiandituBasemap = new Basemap({
            baseLayers: [vecLayer],
            referenceLayers: [cvaLayer],
            title: "天地图矢量图(WGS84)",
            id: "tianditu-vector-wgs84"
        });
        return {
            tileInfo,
            config,
            getUrlTemplate,
            tiandituBasemap,
            WebTileLayer,
            Basemap,
            vecLayer,
            cvaLayer
        };
    } catch (error) {
        console.error("天地图加载失败:", error);
        throw new Error("天地图公共模块加载异常,请检查依赖和配置");
    }
}
posted @ 2025-10-21 17:09  yxysuanfa  阅读(10)  评论(0)    收藏  举报