目录
- 引言
- 一、为什么要开发图层透明度控制功能
- 二、开发思路整理
- 1. cesium图层api查询
- 1.1 imageryLayer 透明度
- 1.2 primitive 透明度
- 三、代码编写
- 1. 修改原有图层管理代码
- 2. 新增页面结构
- 3. 编写图层透明度控制方法
- 四、总结
引言
本教程主要是围绕Cesium这一开源三维框架开展的可视化项目教程。总结一下相关从业经验,如果有什么疑问或更好的见解,欢迎评论、私聊探讨,共同进步。教程依托于vue3前端框架,参考初始化内容:【WebGis开发 - Cesium】三维可视化项目教程—初始化场景
在【WebGis开发 - Cesium】三维可视化项目教程—图层管理基础 文中我们已经搭建了图层管理的基础框架代码。在本篇中我们将拓展图层透明度控制功能。
先看效果:
一、为什么要开发图层透明度控制功能
cesium三维场景不可避免地会存在遮挡关系,二维图层因为添加顺序问题存在层级遮挡,三维图层因为物理长宽高相互遮挡。在实际应用场景中,我们常常需要两个图层之间互作对比,通过将其中一个图层半透明来突出与另一个图层的位置关系。根据现有图层管理的图层类型,我们大致可以将图层分为 imageryLayer
、primitiveLayer
这两种(后者是不存在的,只是做分类起名字)。
- 二维图层例如:
wmts
、wms
、mapserver
等都会以imageryLayer
形式存储在cesium实例中。 - 三维图层例如:
3dtiles
会以primitive
形式存储在cesium实例中。
大致上图层会以imageryLayer
、 primitive
这两种形式存在,所以在考虑设置透明度时,可以针对这两种类型分别编写对应方法。
二、开发思路整理
首先整理一下要实现这个功能需要准备哪些东西:
- 一个直观的滑动条组件,将滑动选择的透明度值输入给图层材质。本文中使用
element-plus
的slider
组件。 - 查询cesium针对
imageryLayer
、primitive
这两种形式的透明材质如何设置。
1. cesium图层api查询
1.1 imageryLayer 透明度
文档地址:imageryLayer图层文档
通过传递[0.0 , 1.0]区间内的浮点数设置图层透明度。
有一个测试用的小技巧,当你在初始化cesium场景时设置了window.viewer
。那么你可以通过控制台输入 viewer.imageryLayers._layers
得到imageryLayer
数组。打开其中一个,找到 alpha
属性,双击属性值可以动态输入一个新值用来测试。回车键确定输入值,场景中如果该图层在最上层,那么将能看到它的透明度变为原来的一半(临时修改,刷新页面效果即消失)。
1.2 primitive 透明度
primitive
三维图层设置透明度和 imageryLayer
略有不同,它没有显式的提供一个 alpha
属性属性用来修改透明度。我们需要从3dtiles实例对象中设置style属性。
通过查询 viewer.scene.primitives._primitives
可以获取到当前已经加载的3dtiles图层有哪些。查询指定图层的style属性发现是 undefined
,此时我们需要自定义一个 Cesium3DTileStyle
示例赋予指定图层的style属性。参考:Cesium3DTileStyle参考文档
三、代码编写
1. 修改原有图层管理代码
为了适应图层透明度调整的功能需求,我们仍需在原有的图层管理代码中做少许微调。
调整hooks/useLayerManager.js文件内容:
// 修改 addWmtsLayer 方法const addWmtsLayer = (data) => {// 其他代码保持原样// 向全局状态输入图层数据,新增默认不透明度为1layerStore.addImageryLayer({...data,show: true,opacity: 1,layerId: layer.layerId,});};
// 修改 add3dtilesLayer 方法const add3dtilesLayer = async (data) => {// 其他代码保持原样// 向全局状态输入图层数据,新增默认不透明度为1layerStore.addPrimitiveLayer({...data,show: true,opacity: 1,layerId: tileset.layerId,});viewer.flyTo(tileset);};
这里添加opacity字段是为了与已选图层中的slider组件做双向绑定。
2. 新增页面结构
在原有图层管理的页面中已选图层拖拽组件上继续修改页面结构:
- 改造dragItem,将其分为上下两层,上层为标题和删除按钮,下层为透明度控制slider。
- 在slider中我使用了
change
方法,在拖拽结束鼠标松开时触发透明度更新。如果你的场景不复杂且电脑配置足够高,可以替换为input
方法,鼠标拖动slider实时控制图层透明度变化。 - 在图层树添加
ref="layerTree"
配合删除按钮做勾选框与图层删除联动效果。
<el-treeref="layerTree"style="max-width: 300px":data="dataSource"show-checkboxnode-key="id"default-expand-allcheck-on-click-node@check="onCheck"@check-change="checkChange":expand-on-click-node="false"></el-tree><div>已选图层</div><draggablev-model="layerStore.imageryLayers"@change="dragChange"item-key="id"ghostClass="ghost"><template #item="{ element }"><div class="dragItem"><div class="dragItem_title"><span>{{ element.label }}</span><imgsrc="@/assets/icons/delete.svg"alt="delete"@click="deleteLayer(element)"/></div><el-slider:min="0":max="1":step="0.1"v-model="element.opacity"@change="changeOpacity(element)"/></div></template></draggable>
新增js方法完成页面部分
- 从
useLayerManager.js
文件中新引入setLayerOpacity
通用方法,用于设置图层透明度。 - 配合
el-tree
的ref
属性,定义组件dom的引用。利用setChecked
方法将删除图层手动去掉勾选,完成树结构勾选框与已选列表同步效果。
import { useLayerManager } from "@/hooks/useLayerManager.js";
const { removeLayer, setLayerOpacity} = useLayerManager();
const changeOpacity = (data) => {console.log("changeOpacity", data);setLayerOpacity(data);
};const layerTree = ref(null);
const deleteLayer = (data) => {removeLayer(data);layerTree.value.setChecked(data.id, false, true);
};
3. 编写图层透明度控制方法
在hooks/useLayerManager.js文件中继续添加方法,注意最后将方法导出:
对外暴露设置图层不透明度通用方法,根据类型选择具体方法。
/*** @description: 设置图层不透明度通用方法* @param {*} data* @return {*}*/const setLayerOpacity = (data) => {return setLayerOpacityFunctions[data.type](data);};return {setLayerOpacity }
创建不透明度方法调度对象,存储具体方法
/*** @description: 设置图层不透明度方法map,用于存储不同图层设置不透明度方法* @return {*}*/const setLayerOpacityFunctions = {"wmts": setImageryLayerOpacity,"3dtiles": setPrimitiveLayerOpacity,};
创建具体不同类型图层不透明度设置方法
/*** @description: 设置ImageryLayer图层不透明度* @param {*} data* @return {*}*/const setImageryLayerOpacity = (data) => {const layerData = layerStore.getImageryLayer(data.id);if (layerData) {const layer = viewer.imageryLayers._layers.find((item) => item.layerId === layerData.layerId);layer.alpha = data.opacity;}};/*** @description: 设置PrimitiveLayer图层不透明度* @param {*} data* @return {*}*/const setPrimitiveLayerOpacity = (data) => {const layerData = layerStore.getPrimitiveLayer(data.id);if (layerData) {const layer = viewer.scene.primitives._primitives.find((item) => item.layerId === layerData.layerId);layer.style = new Cesium.Cesium3DTileStyle({color: `color('rgba(255,255,255,${data.opacity})')`,});}};
至此就完成了已选图层透明度控制,以及图层手动删除的功能拓展。
四、总结
本篇主要是利用 element-plus
的 slider
组件配合 pinia
存储的数据列表做双向绑定,完成图层透明度控制功能。利用 tree
组件的 setChecked
方法,完成已选图层与图层树的删除联动效果。根据功能拓展的需求,将原有图层管理操作逻辑进行修改完善以适应新功能的接入。重点在于根据业务的需求逻辑,封装cesium提供的原始api方法,形成维度更高的项目级api方法。
再接再厉~