javascript
复制代码
<template>
<div class="el-main">
<!-- Cesium 容器 -->
<div id="cesiumContainer"></div>
<!-- 卷帘滑块 -->
<div id="slider"></div>
<!-- Leaflet 地图容器 -->
<div id="leafletContainer"></div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import * as Cesium from "cesium";
import L from "leaflet"; // 引入 Leaflet
// 设置 Cesium Ion 的默认访问令牌
Cesium.Ion.defaultAccessToken = "youKey";
onMounted(() => {
init();
});
let leafletUpdating = ref(false); // 标志位,防止循环触发
let leafletMap;
const init = () => {
const viewer = initializeCesiumViewer();
const leafletMap = initializeLeafletMap();
const slider = document.getElementById("slider");
// 监听cesium相机变化事件
viewer.camera.percentageChanged = 0.00001;
viewer.camera.changed.addEventListener(() => {
leafletUpdating.value = false;
const centerPosition = getCenterPosition(viewer);
if (centerPosition) {
const zoomLevel = calculateZoomLevel(
viewer.camera.positionCartographic.height
);
leafletMap.setView(
[centerPosition.lat, centerPosition.lng],
zoomLevel + 4
);
leafletUpdating.value = true;
}
});
// 监听leaflet相机变化事件
let splitPosition = 0.5; // 初始分屏位置为 50%
updateSplitPosition(splitPosition);
const handler = new Cesium.ScreenSpaceEventHandler(slider);
let moveActive = false;
// 滑块移动逻辑
const move = (movement) => {
// 如果没有激活移动状态,则直接返回
if (!moveActive) return;
// 获取滑块父元素的宽度
const parentWidth = slider.parentElement.offsetWidth;
// 计算新的分割位置,确保其在0到1之间
const newSplitPosition = Math.min(
// slider.offsetLeft 获取滑块当前的左边距(以像素为单位)。
// movement.endPosition.x 获取鼠标或触摸事件的移动距离(以像素为单位)。
Math.max((slider.offsetLeft + movement.endPosition.x) / parentWidth, 0),
1
);
// 更新分割位置
updateSplitPosition(newSplitPosition);
};
// 监听鼠标按下事件(开始拖动)
handler.setInputAction(() => {
moveActive = true;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
// 监听鼠标移动事件(拖动过程中更新分屏位置)
handler.setInputAction(move, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 监听鼠标松开事件(结束拖动)
handler.setInputAction(() => {
moveActive = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
// 监听 Leaflet 的 zoomend 事件
leafletMap.on("zoomend", (e) => {
const center = leafletMap.getCenter();
const scale = e.target.getZoom();
console.log("leaflet坐标和层级:", center, scale);
});
// 将 viewer 挂载到全局 window 对象上,便于调试
window.viewer = viewer;
};
/**
* 初始化 Cesium Viewer 实例
* @returns {Cesium.Viewer} Cesium Viewer 实例
*/
const initializeCesiumViewer = () => {
const viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false,
geocoder: false,
timeline: false,
animation: false,
});
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
116.38225078582765,
39.90710270565395,
4500
),
orientation: {
heading: 6.283185307179581,
pitch: -1.5688168484696687,
roll: 0.0,
},
});
var layer = new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
minimumLevel: 4,
maximumLevel: 18,
});
viewer.imageryLayers.addImageryProvider(layer);
viewer.camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; // 约束相机只能垂直缩放
viewer.scene.screenSpaceCameraController.zoomFactor = 0.1; // 设置缩放速率
return viewer;
};
/**
* 初始化 Leaflet 地图
* @returns {L.Map} Leaflet 地图实例
*/
const initializeLeafletMap = () => {
leafletMap = L.map("leafletContainer").setView(
[39.90710270565395, 116.38225078582765],
15
);
const layer = L.tileLayer(
"http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
{
subdomains: ["1", "2", "3", "4"],
minZoom: 1,
maxZoom: 19,
}
);
leafletMap.addLayer(layer);
return leafletMap;
};
/**
* 更新分屏位置
* @param {number} position - 分屏位置(0 到 1 之间的值)
*/
const updateSplitPosition = (position) => {
const slider = document.getElementById("slider");
slider.style.left = `${100 * position}%`;
document.getElementById("leafletContainer").style.width = `${
100 * (1 - position)
}%`;
document.getElementById("leafletContainer").style.clipPath = `inset(0 0 0 ${
100 * position
}%)`;
document.getElementById("cesiumContainer").style.width = `${100 * position}%`;
document.getElementById("cesiumContainer").style.clipPath = `inset(0 ${
100 * (1 - position)
}% 0 0)`;
};
/**
* 获取屏幕中心点的地理坐标
* @param {Cesium.Viewer} viewer - Cesium Viewer 实例
* @returns {Object|null} 包含经度和纬度的对象,或 null
*/
const getCenterPosition = (viewer) => {
const centerResult = viewer.camera.pickEllipsoid(
new Cesium.Cartesian2(
viewer.canvas.clientWidth / 2,
viewer.canvas.clientHeight / 2
)
);
if (centerResult) {
const cartographic =
Cesium.Ellipsoid.WGS84.cartesianToCartographic(centerResult);
return {
lng: Cesium.Math.toDegrees(cartographic.longitude),
lat: Cesium.Math.toDegrees(cartographic.latitude),
};
}
return null;
};
/**
* 计算近似的缩放级别
* @param {number} height - 相机高度(米)
* @returns {number} 缩放级别
*/
const calculateZoomLevel = (height) => {
const earthRadius = 6378137; // 地球半径(米)
return Math.round(Math.log2((2 * earthRadius) / height));
};
// 将Leaflet的缩放级别转换为Cesium的相机高度
const calculateHeightFromZoom = (zoomLevel) => {
const earthRadius = 6378137; // 地球半径(米)
return (2 * earthRadius) / Math.pow(2, zoomLevel);
};
</script>
<style scoped>
/* 卷帘滑块样式 */
#slider {
position: absolute;
left: 50%;
top: 0;
background-color: red;
width: 3px;
height: 100%;
cursor: ew-resize;
z-index: 10;
}
/* Leaflet 地图容器样式 */
#leafletContainer {
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 100% !important;
clip-path: inset(0 0 0 50%);
}
/* Cesium 地图容器样式 */
#cesiumContainer {
position: absolute;
top: 0;
bottom: 0;
left: 0;
/* width: 50%; */
z-index: 10;
width: 100% !important;
clip-path: inset(0 50% 0 0);
}
/* 主内容区域样式 */
.el-main {
position: relative;
width: 100%;
height: 100%;
}
</style>