353 lines
7.5 KiB
Vue
353 lines
7.5 KiB
Vue
<template>
|
||
<div class="zoom-control">
|
||
<LayerSwitcher
|
||
class="layer-switcher"
|
||
:map="map"
|
||
@layerChange="onLayerChange"
|
||
ref="layerSwitcherRef"
|
||
/>
|
||
|
||
<div
|
||
class="compass-container"
|
||
:style="{ transform: `rotate(${-rotation}deg)` }"
|
||
>
|
||
<div class="rotate-left" @click="rotateLeft" title="向右旋转90°"></div>
|
||
|
||
<div class="rotate-right" @click="rotateRight" title="向左旋转90°"></div>
|
||
</div>
|
||
<div class="zoom-in" @click="onZoomIn"></div>
|
||
<div class="zoom-out" @click="onZoomOut"></div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted, watch } from "vue";
|
||
import LayerSwitcher from "./LayerSwitcher.vue";
|
||
|
||
// 导入图片资源
|
||
import znzBgImg from "../../../assets/images/znzBg.png";
|
||
import needleImg from "../../../assets/images/znz.png";
|
||
import leftArrowImg from "../../../assets/images/left_arrow.png";
|
||
import rightArrowImg from "../../../assets/images/right_arrow.png";
|
||
|
||
// 定义props接受地图实例
|
||
const props = defineProps({
|
||
map: Object, // 地图实例
|
||
resetView: {
|
||
type: Function,
|
||
default: null,
|
||
},
|
||
});
|
||
|
||
// 组件引用
|
||
const layerSwitcherRef = ref(null);
|
||
|
||
// 地图旋转角度 (弧度转换为度)
|
||
const rotation = ref(0);
|
||
|
||
// 向父组件发出事件
|
||
const emit = defineEmits([
|
||
"compass",
|
||
"zoomIn",
|
||
"zoomOut",
|
||
"layerChange",
|
||
"rotate",
|
||
]);
|
||
|
||
// 监听地图视图变化,更新旋转角度
|
||
function updateRotation() {
|
||
if (props.map && props.map.getView()) {
|
||
// OpenLayers中旋转角度是弧度,转换为度
|
||
rotation.value = (props.map.getView().getRotation() * 180) / Math.PI;
|
||
}
|
||
}
|
||
|
||
// 重置地图旋转
|
||
function resetRotation() {
|
||
if (props.map) {
|
||
// 重置旋转为0
|
||
props.map.getView().animate({
|
||
rotation: 0,
|
||
duration: 300,
|
||
});
|
||
emit("rotate", 0);
|
||
}
|
||
}
|
||
|
||
// 向左旋转地图(逆时针)
|
||
function rotateLeft() {
|
||
if (props.map) {
|
||
const currentRotation = props.map.getView().getRotation();
|
||
const newRotation = currentRotation - Math.PI / 2; // 旋转90度(逆时针)
|
||
props.map.getView().animate({
|
||
rotation: newRotation,
|
||
duration: 300,
|
||
});
|
||
emit("rotate", (newRotation * 180) / Math.PI);
|
||
}
|
||
}
|
||
|
||
// 向右旋转地图(顺时针)
|
||
function rotateRight() {
|
||
if (props.map) {
|
||
const currentRotation = props.map.getView().getRotation();
|
||
const newRotation = currentRotation + Math.PI / 2; // 旋转90度(顺时针)
|
||
props.map.getView().animate({
|
||
rotation: newRotation,
|
||
duration: 300,
|
||
});
|
||
emit("rotate", (newRotation * 180) / Math.PI);
|
||
}
|
||
}
|
||
|
||
// 指南针/重置视图
|
||
function onCompass() {
|
||
emit("compass");
|
||
if (props.resetView) {
|
||
props.resetView();
|
||
} else if (props.map) {
|
||
// 如果没有提供重置函数,尝试使用地图的初始视图
|
||
const initialCenter = props.map.options.center;
|
||
const initialZoom = props.map.options.zoom || 7;
|
||
props.map.setView(initialCenter, initialZoom, { animate: true });
|
||
}
|
||
}
|
||
|
||
// 放大地图
|
||
function onZoomIn() {
|
||
emit("zoomIn");
|
||
if (props.map) {
|
||
const currentZoom = props.map.getView().getZoom();
|
||
props.map.getView().animate({
|
||
zoom: currentZoom + 1,
|
||
duration: 250,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 缩小地图
|
||
function onZoomOut() {
|
||
emit("zoomOut");
|
||
if (props.map) {
|
||
const currentZoom = props.map.getView().getZoom();
|
||
props.map.getView().animate({
|
||
zoom: currentZoom - 1,
|
||
duration: 250,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 图层变化处理
|
||
function onLayerChange(layerInfo) {
|
||
console.log("ZoomControl收到图层变化:", layerInfo);
|
||
// 将图层信息传递给父组件
|
||
emit("layerChange", layerInfo);
|
||
}
|
||
|
||
// 监听地图旋转事件
|
||
let rotationListener = null;
|
||
|
||
onMounted(() => {
|
||
if (props.map) {
|
||
// 初始化旋转角度
|
||
updateRotation();
|
||
|
||
// 添加视图变化监听器
|
||
rotationListener = props.map
|
||
.getView()
|
||
.on("change:rotation", updateRotation);
|
||
}
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
// 移除监听器
|
||
if (rotationListener) {
|
||
rotationListener.dispose();
|
||
}
|
||
});
|
||
|
||
// 监听地图实例变化
|
||
watch(
|
||
() => props.map,
|
||
(newMap) => {
|
||
if (newMap) {
|
||
// 初始化旋转角度
|
||
updateRotation();
|
||
|
||
// 移除旧的监听器
|
||
if (rotationListener) {
|
||
rotationListener.dispose();
|
||
}
|
||
|
||
// 添加新的视图变化监听器
|
||
rotationListener = newMap.getView().on("change:rotation", updateRotation);
|
||
}
|
||
}
|
||
);
|
||
|
||
// 导出方法供外部调用
|
||
defineExpose({
|
||
// 设置图层可见性的方法
|
||
setLayerVisibility(layerName, visible) {
|
||
if (layerSwitcherRef.value) {
|
||
return layerSwitcherRef.value.setLayerVisibility(layerName, visible);
|
||
}
|
||
return false;
|
||
},
|
||
// 重置旋转
|
||
resetRotation,
|
||
// 获取当前旋转角度
|
||
getRotation() {
|
||
return rotation.value;
|
||
},
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.zoom-control {
|
||
position: absolute;
|
||
bottom: 250px;
|
||
left: 20px;
|
||
z-index: 3000;
|
||
width: 40px;
|
||
height: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
gap: 25px;
|
||
}
|
||
|
||
.compass-container {
|
||
position: relative;
|
||
width: 40px;
|
||
height: 40px;
|
||
cursor: pointer;
|
||
background: url("../../../assets/images/znzBg.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.rotation-controls {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
z-index: 3002;
|
||
}
|
||
|
||
.rotate-left,
|
||
.rotate-right {
|
||
width: 24px;
|
||
height: 24px;
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.rotate-left {
|
||
left: -3px;
|
||
background: url("../../../assets/images/left_arrow.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
z-index: 5000;
|
||
}
|
||
|
||
.rotate-right {
|
||
right: -3px;
|
||
background: url("../../../assets/images/right_arrow.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.rotate-left:hover,
|
||
.rotate-left:active {
|
||
left: -3px;
|
||
background: url("../../../assets/images/left_arrow_active.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
.rotate-right:hover,
|
||
.rotate-right:active {
|
||
right: -3px;
|
||
background: url("../../../assets/images/right_arrow_active.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.layer-switcher {
|
||
z-index: 3000;
|
||
width: 40px;
|
||
height: 40px;
|
||
background: url("../../../assets/images/layer.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
cursor: pointer;
|
||
}
|
||
.layer-switcher:hover,
|
||
.layer-switcher:active {
|
||
background: url("../../../assets/images/layerActive.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.zoom-in {
|
||
background: url("../../../assets/images/zoomOut.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
z-index: 3000;
|
||
width: 40px;
|
||
height: 40px;
|
||
cursor: pointer;
|
||
}
|
||
.zoom-in:hover,
|
||
.zoom-in:active {
|
||
background: url("../../../assets/images/zoomOutActive.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.zoom-out {
|
||
background: url("../../../assets/images/zoomIn.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
z-index: 3000;
|
||
width: 40px;
|
||
height: 40px;
|
||
cursor: pointer;
|
||
}
|
||
.zoom-out:hover,
|
||
.zoom-out:active {
|
||
background: url("../../../assets/images/zoomInActive.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 768px) {
|
||
.zoom-in,
|
||
.zoom-out,
|
||
.compass-container,
|
||
.compass-bg,
|
||
.compass-needle {
|
||
width: 28px;
|
||
height: 28px;
|
||
}
|
||
|
||
.zoom-in,
|
||
.zoom-out {
|
||
background-size: 80%;
|
||
background-position: center center;
|
||
background-repeat: no-repeat;
|
||
}
|
||
|
||
.rotate-left,
|
||
.rotate-right {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
.rotate-left {
|
||
left: -18px;
|
||
}
|
||
|
||
.rotate-right {
|
||
right: -18px;
|
||
}
|
||
}
|
||
</style>
|