前言
上一篇博客简单得介绍了影像图层并成功在视图上加载出来了,而今天我们来实现一个简单的可视化效果,影像图层卷帘。
前置知识:Cesium 事件详解(鼠标事件、相机事件、键盘事件、场景触发事件)_cesium点击事件_GISer小辉的博客-CSDN博客
代码
参考沙盒示例:Cesium Sandcastle
实现步骤:
- 创建卷帘分割线的div元素
- 加载2个影像图层并分别设置切割的方向(Cesium.SplitDirection)
- 为分割线div绑定鼠标事件
- 在鼠标位移时修改分割线的位置和场景的分割位置
import * as Cesium from 'cesium'
import { map as viewer } from '@/utils/createCesium.js'export function ImagerySplit (target = 'cesiumContainer') {const cesiumCon = document.getElementById(target) // 获取地球渲染的targetconst slider = document.createElement('div')const sliderWidth = '5px' // 分割线的宽度slider.id = 'slider'slider.style.width = sliderWidthslider.style.height = '100%'slider.style.position = 'fixed'slider.style.left = '50%'slider.style.top = '0'slider.style.backgroundColor = '#3370FF'slider.style.zIndex = '10'slider.style.cursor = 'col-resize'// 将滑动条添加到页面中的某个元素内if (cesiumCon) {cesiumCon.appendChild(slider) // 将slider元素添加到father元素的子元素列表中} else {document.querySelector('body').appendChild(slider)}// 左边的图层,標準風格const left = viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({url: 'https://tile-{s}.openstreetmap.fr/hot/{z}/{x}/{y}.png',subdomains: ['a', 'b', 'c', 'd']}))// 右边的图层,黑夜風格const right = viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',subdomains: ['a', 'b', 'c', 'd']}))// 设置切割的方向left.splitDirection = Cesium.SplitDirection.LEFTright.splitDirection = Cesium.SplitDirection.RIGHT// 依据滑动条的默认位置设置场景的分割位置,值为0到1之间viewer.scene.splitPosition = slider.offsetLeft / slider.parentElement.offsetWidthlet moveActive = false// 滑动事件: 修改滑动条的位置以及视图分割的位置function moveEvnet (movement) {if (!moveActive) {return}const relativeOffset = movement.endPosition.xconst splitPosition = (slider.offsetLeft + relativeOffset) / slider.parentElement.offsetWidthslider.style.left = `${100.0 * splitPosition}%`viewer.scene.splitPosition = splitPosition}// 中間那個分割綫的句柄const handler = new Cesium.ScreenSpaceEventHandler(slider)handler.setInputAction(() => {moveActive = true}, Cesium.ScreenSpaceEventType.LEFT_DOWN) // 未适配PINCH、PINCH_START等操作handler.setInputAction(moveEvnet, Cesium.ScreenSpaceEventType.MOUSE_MOVE)handler.setInputAction(() => {moveActive = false// 不要让分割线超出界面了if (parseFloat(slider.style.left.replace('%', '')) > 100) {slider.style.left = `calc(100% - ${sliderWidth})`} else if (parseFloat(slider.style.left.replace('%', '')) < 0) {slider.style.left = '0%'}}, Cesium.ScreenSpaceEventType.LEFT_UP)
}
写完整一点
代码提交参考: feat: 新增影像卷帘方法及组件 · 4bb13cb · ReBeX/cesium-tyro-blog - Gitee.com
ok,结合我们项目已有的方法,让我来写一个完整点的影像图层卷帘方法。
先写一个类
export class splitImagery {// !属性slider // 滑动分割线的div元素sliderWidth // 分割线的宽度target // 渲染cesium场景的元素leftImagery // 左边的影像rightImagery // 右边的影像moveActive = false // 开启分割线位移imageryLayers = [] // 当前场景已有的影像图层constructor(target = 'cesiumContainer') {this.target = target}// !方法......
}
然后我们往这个类里头加入方法
创建分割线的div元素
spilitElement() {const cesiumCon = document.getElementById(this.target) // 获取地球渲染的targetthis.slider = document.createElement('div')this.sliderWidth = '5px' // 分割线的宽度this.slider.id = 'slider'this.slider.style.width = this.sliderWidththis.slider.style.height = '100%'this.slider.style.position = 'fixed'this.slider.style.left = '50%'this.slider.style.top = '0'this.slider.style.backgroundColor = '#3370FF'this.slider.style.zIndex = '10'this.slider.style.cursor = 'col-resize'// 将滑动条添加到页面中的元素内if (cesiumCon) {cesiumCon.appendChild(this.slider)} else {document.querySelector('body').appendChild(this.slider)}}
设置分割线左侧展示的影像
setLeftImagery(layer = loadImagery.ion('', 3812)) {if (this.leftImagery) { // 如果已经有图层了则销毁旧的加载新的viewer.imageryLayers.remove(this.leftImagery, true); // 移除图层}this.leftImagery = layer// viewer.imageryLayers.add(layer)this.leftImagery.splitDirection = Cesium.SplitDirection.LEFT // 分割方向console.log('this.leftImagery: ', this.leftImagery);}
设置分割线右侧展示的影像
setRightImagery(layer = loadImagery.ion('', 3845)) {if (this.rightImagery) {viewer.imageryLayers.remove(this.rightImagery, true); // 移除图层}this.rightImagery = layer// viewer.imageryLayers.add(layer)this.rightImagery.splitDirection = Cesium.SplitDirection.RIGHT // 分割方向}
获取当前场景中所有的影像图层并保存到数组中
saveImageryLayers() {const layers = viewer.imageryLayers;for (let i = 0; i < layers.length; i++) {console.log('layers.get(i): ', layers.get(i));this.imageryLayers.push(layers.get(i));}layers.removeAll(false) // 移除所有 ImageryLayer}
重新加载之前保存的影像图层
reloadImageryLayers() {const layers = viewer.imageryLayers;layers.removeAll(false) // 移除所有 ImageryLayerfor (let i = 0; i < this.imageryLayers.length; i++) {layers.add(this.imageryLayers[i]);}}
滑动事件: 修改滑动条的位置以及视图分割的位置
_moveEvnet(movement) {if (!this.moveActive) {return}const relativeOffset = movement.endPosition.xconst splitPosition = (this.slider.offsetLeft + relativeOffset) / this.slider.parentElement.offsetWidththis.slider.style.left = `${100.0 * splitPosition}%`viewer.scene.splitPosition = splitPosition}
开始卷
actionSplit() {this.saveImageryLayers()this.spilitElement()this.setLeftImagery()this.setRightImagery()// 依据滑动条的默认位置设置场景的分割位置,值为0到1之间viewer.scene.splitPosition = this.slider.offsetLeft / this.slider.parentElement.offsetWidththis.handler = new Cesium.ScreenSpaceEventHandler(this.slider)this.handler.setInputAction(() => {this.moveActive = true}, Cesium.ScreenSpaceEventType.LEFT_DOWN) // 未适配PINCH、PINCH_START等操作this.handler.setInputAction(this._moveEvnet.bind(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE)this.handler.setInputAction(() => {this.moveActive = false// 不要让分割线超出界面了if (parseFloat(this.slider.style.left.replace('%', '')) > 100) {this.slider.style.left = `calc(100% - ${this.sliderWidth})`} else if (parseFloat(this.slider.style.left.replace('%', '')) < 0) {this.slider.style.left = '0%'}}, Cesium.ScreenSpaceEventType.LEFT_UP)}
不卷了
stopSplit() {if (!this.slider) {return}this.reloadImageryLayers()this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN)this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)const cesiumCon = document.getElementById(this.target) // 获取地球渲染的targetif (cesiumCon) {cesiumCon.removeChild(this.slider)} else {document.querySelector('body').removeChild(this.slider)}}
调用
import {splitImagery}from '@/utils/ImageryLayer/splitImagery.js'const splitInstance = new splitImagery() // 声明实例splitInstance.actionSplit() // 开启卷帘功能
splitInstance.stopSplit() // 关闭卷帘功能
splitInstance.setLeftImagery(loadImagery.osm()) // 修改卷帘左侧的影像,loadImagery.osm()是上一篇博客写的方法,会加载并返回影像图层