🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:gis实践
🌈 若有帮助,还请关注 ➕ 点赞➕收藏,不行的话我再努努力💪💪💪
本文基于 Cesium 1.132 版本进行演示,其他版本在 API 上可能略有不同。
本文整理了css和cesium 卷帘效果应用,配合示例代码,你可以在实际项目中直接使用或二次封装,提升开发效率。
✨ 欢迎大家在评论区提出建议或交流心得,如果需要完整源码及css资源,可以私聊我获取。
原生 CSS 的卷帘实现
效果
<template>
<div class="mapClip-wrap" ref="mapClipRef">
<div class="image-box image-left" :style="{ clip: `rect(0px, ${leftPos}px, 570px, 0px)`}">
<img src="@/assets/images/ui/sun-left.jpg"/>
<p>水效:关闭</p>
</div>
<div class="image-box image-right">
<img src="@/assets/images/ui/sun-right.jpg"/>
<p>水效:打开</p>
</div>
<div class="control-handle" :style="{left:leftPos + 'px'}"
@mousedown="mousedownFn">
<el-icon size="18">
<ArrowLeftBold class="icon left"/>
</el-icon>
<el-icon size="18">
<ArrowRightBold class="icon right"/>
</el-icon>
<div data-v-64761556="" class="line"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import {reactive, ref, toRefs} from "vue";
// Ref
const mapClipRef = ref(null)
const model = reactive({
draggingType: "",
leftPos: 100,
})
const {leftPos} = toRefs(model)
const mousedownFn = () => {
model.draggingType = '1'
document.addEventListener('mousemove', mousemoveFn)
document.addEventListener('mouseup', mouseupFn)
}
const mousemoveFn = (e) => {
if (!model.draggingType) return
const {left, width} = mapClipRef.value.getBoundingClientRect()
switch (true) {
case e.clientX - left < 0:
model.leftPos = 0
break
case e.clientX - left > width :
model.leftPos = width
break
default:
model.leftPos = e.clientX - left
break
}
}
const mouseupFn = () => {
model.draggingType = ""
document.removeEventListener('mousemove', mousemoveFn)
document.removeEventListener('mouseup', mouseupFn)
}
</script>
适用场景
前端页面、二维地图对比、轻量级可视化、移动端页面
实现思路
原生 CSS 的卷帘,本质是在两张叠加的图片之间,通过修改上层图片的 可视区域 来实现左右拖动的对比效果。
CSS 卷帘的核心就是:
- 将两层地图图片叠放(z-index)
- 📌上层地图用 clip 控制裁剪宽度
- 与拖动条同步裁剪区域
- 通过监听 mousemove 或拖拽条位置动态更新裁剪范围
- 不依赖任何框架,非常轻量级 ⚡
Cesium 的地图卷帘实现
效果
<template>
<div class="mapSplit-wrap">
<div class="slider" ref="sliderRef" @mouseup="moveActive = false">
<div class="sliderImg" @mousedown="moveActive = true"></div>
</div>
<!-- 默认加载天地图-->
<Tdt_img_d/>
</div>
</template>
<script lang="ts" setup>
import {usemapStore} from "@/store/modules/cesiumLastMap";
import GUI from "lil-gui";
import {onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import * as Cesium from "cesium";
// Component
import Tdt_img_d from "@/views/cesiumLast/component/main/controlPanel/layerManagement/basicMap/tdt_img_d.vue"
// Ref
const sliderRef = ref(null)
const mapStore = usemapStore()
const model = reactive({
moveActive: false
})
const {moveActive} = toRefs(model)
onMounted(async () => {
initGui()
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
viewer.scene.splitPosition = sliderRef.value.offsetLeft / viewer.scene.canvas.offsetWidth;
handler.setInputAction(onMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
addSplitDirection()
})
onUnmounted(() => {
gui.destroy()
})
// 地图逻辑
const viewer = mapStore.getCesiumViewer()
let handler, imageryProviderL, imageryProviderR
const addSplitDirection = async () => {
imageryProviderL = await viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: 'https://{s}.tianditu.gov.cn/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=' + import.meta.env.VITE_APP_TDT_KEY,
layer: 'vec_d',
style: 'default',
format: 'image/jpeg',
tileMatrixSetID: 'GoogleMapsCompatible',
subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
maximumLevel: 18
}));
imageryProviderL.splitDirection = -1
imageryProviderR = await viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: 'https://{s}.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=' + import.meta.env.VITE_APP_TDT_KEY,
layer: 'img_d',
style: 'default',
format: 'image/jpeg',
tileMatrixSetID: 'GoogleMapsCompatible',
subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
maximumLevel: 18
}));
imageryProviderR.splitDirection = 1
}
const onMouseMove = (movement) => {
if (!model.moveActive) return
const splitPosition = movement.endPosition.x / viewer.scene.canvas.offsetWidth;
sliderRef.value.style.left = `${100 * splitPosition}%`;
viewer.scene.splitPosition = splitPosition;
}
// lil-gui逻辑
let gui
const formData = {
}
const initGui = () => {
gui = new GUI({title: "mapSplit"});
}
</script>
实现思路
Cesium 提供内置的卷帘能力,通过 viewer.scene.splitPosition 控制三维场景中左右图层的切割比例。
- 添加两个影像图层 A、B
- 为 A 设置:layer.splitDirection = Cesium.SplitDirection.LEFT
- 为 B 设置:layer.splitDirection = Cesium.SplitDirection.RIGHT
- 📌通过 viewer.scene.splitPosition = x 控制切割位置(0 ~ 1)
- 随着滑块拖动动态调整 splitPosition,实现三维地图卷帘⚡
适用场景
三维水利、遥感对比、地形变化分析、城市规划、倾斜摄影前后对比
Takeaways
地图卷帘(Map Swipe / Split View)是一种常见的时空对比方式,被广泛用于遥感解译、水利监测、规划对比等场景。
🌐 在 CSS 方案中,我们通过 clip 完成轻量卷帘,非常适合网页展示与 UI 控件自定义。
🛰️ 在 Cesium 方案中,我们利用 viewer.scene.splitPosition 实现专业级的三维卷帘能力,可对比影像、地形、倾斜模型等多源数据。
无论你是做 Web 可视化、数字孪生、还是 GIS 系统开发,这两种方案都能在实际项目中带来清晰、高效的数据对比体验。
1871

被折叠的 条评论
为什么被折叠?



