|
|
|
@ -1,5 +1,5 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
import { onMounted } from 'vue' |
|
|
|
import { onMounted, watch, ref } from 'vue' |
|
|
|
import { Viewer, ScreenSpaceEventHandler, ScreenSpaceEventType, defined, Cartographic, sampleTerrainMostDetailed, Cartesian3, Math as cesiumMath, Ion } from 'cesium' |
|
|
|
import { Wayline } from './wayline' |
|
|
|
import { useStore } from './store' |
|
|
|
@ -7,6 +7,142 @@ import { createViewer } from './basemap' |
|
|
|
import * as egm96 from 'egm96-universal' |
|
|
|
|
|
|
|
const store = useStore() |
|
|
|
const isDraggingSlider = ref(false) |
|
|
|
const isMinimapExpanded = ref(false) |
|
|
|
|
|
|
|
// 声明全局变量类型 |
|
|
|
declare global { |
|
|
|
interface Window { |
|
|
|
wayline?: Wayline |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 监听镜头模式变化,切换时重置变焦 |
|
|
|
watch(() => store.lensMode, (newMode) => { |
|
|
|
if (window.wayline) { |
|
|
|
const minimapViewer = window.wayline.minimapViewer |
|
|
|
if (minimapViewer) { |
|
|
|
function fovFromZoom(zoom: number, fov1: number = 1.3418): number { |
|
|
|
const k = Math.tan(fov1 / 2) |
|
|
|
return 2 * Math.atan(k / zoom) |
|
|
|
} |
|
|
|
if (newMode === 'wide') { |
|
|
|
// 切换到广角模式时,重置为1x |
|
|
|
const currentFov = fovFromZoom(1) |
|
|
|
store.updateZoomFactor(1) |
|
|
|
minimapViewer.camera.frustum.fov = currentFov |
|
|
|
} else { |
|
|
|
// 切换到变焦模式时,如果当前是1x,设置为1x(保持不变) |
|
|
|
if (store.zoomFactor === 1) { |
|
|
|
const currentFov = fovFromZoom(1) |
|
|
|
minimapViewer.camera.frustum.fov = currentFov |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
// 监听变焦倍数变化,更新滑块位置 |
|
|
|
watch(() => store.zoomFactor, () => { |
|
|
|
// 滑块位置会自动通过getSliderPosition()计算更新 |
|
|
|
}) |
|
|
|
|
|
|
|
// 变焦滑块相关函数 |
|
|
|
function getSliderPosition(): number { |
|
|
|
if (store.lensMode === 'wide') { |
|
|
|
return 0 // 广角模式固定在1x位置 |
|
|
|
} |
|
|
|
// 变焦模式:1x在底部(0%),56x在顶部(100%) |
|
|
|
const minZoom = 1 |
|
|
|
const maxZoom = 56 |
|
|
|
const zoom = store.zoomFactor |
|
|
|
const percentage = ((zoom - minZoom) / (maxZoom - minZoom)) * 100 |
|
|
|
// bottom: 0% => 1x, bottom: 100% => 56x |
|
|
|
return percentage |
|
|
|
} |
|
|
|
|
|
|
|
function zoomFromPosition(percentage: number): number { |
|
|
|
// percentage: 0% = 底部(1x), 100% = 顶部(56x) |
|
|
|
const minZoom = 1 |
|
|
|
const maxZoom = 56 |
|
|
|
const zoom = minZoom + (percentage / 100) * (maxZoom - minZoom) |
|
|
|
return Math.round(Math.max(minZoom, Math.min(maxZoom, zoom))) |
|
|
|
} |
|
|
|
|
|
|
|
function onSliderMouseDown(event: MouseEvent) { |
|
|
|
if (store.lensMode === 'wide') return // 广角模式不允许调整 |
|
|
|
const target = event.target as HTMLElement |
|
|
|
if (!target.classList.contains('minimap-zoom-track') && !target.classList.contains('zoom-track')) return |
|
|
|
isDraggingSlider.value = true |
|
|
|
updateZoomFromEvent(event) |
|
|
|
} |
|
|
|
|
|
|
|
function onSliderMouseMove(event: MouseEvent) { |
|
|
|
if (!isDraggingSlider.value || store.lensMode === 'wide') return |
|
|
|
updateZoomFromEvent(event) |
|
|
|
} |
|
|
|
|
|
|
|
function onSliderMouseUp() { |
|
|
|
isDraggingSlider.value = false |
|
|
|
} |
|
|
|
|
|
|
|
function onHandleMouseDown(event: MouseEvent) { |
|
|
|
if (store.lensMode === 'wide') return |
|
|
|
event.stopPropagation() |
|
|
|
isDraggingSlider.value = true |
|
|
|
const handleMouseMove = (e: MouseEvent) => { |
|
|
|
if (isDraggingSlider.value && store.lensMode !== 'wide') { |
|
|
|
// 找到对应的track元素 |
|
|
|
const handle = event.target as HTMLElement |
|
|
|
const track = handle.closest('.minimap-zoom-track') || handle.closest('.zoom-track') |
|
|
|
if (track) { |
|
|
|
const fakeEvent = { ...e, currentTarget: track, target: track } as MouseEvent |
|
|
|
updateZoomFromEvent(fakeEvent) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
const handleMouseUp = () => { |
|
|
|
isDraggingSlider.value = false |
|
|
|
document.removeEventListener('mousemove', handleMouseMove) |
|
|
|
document.removeEventListener('mouseup', handleMouseUp) |
|
|
|
} |
|
|
|
document.addEventListener('mousemove', handleMouseMove) |
|
|
|
document.addEventListener('mouseup', handleMouseUp) |
|
|
|
} |
|
|
|
|
|
|
|
function updateZoomFromEvent(event: MouseEvent) { |
|
|
|
if (store.lensMode === 'wide') return |
|
|
|
|
|
|
|
// 查找滑块轨道元素(支持minimap-zoom-track和zoom-track) |
|
|
|
let sliderElement = (event.currentTarget || event.target) as HTMLElement |
|
|
|
if (!sliderElement || (!sliderElement.classList.contains('zoom-track') && !sliderElement.classList.contains('minimap-zoom-track'))) { |
|
|
|
// 如果点击的是handle,查找父元素中的track |
|
|
|
sliderElement = sliderElement.closest('.minimap-zoom-track') as HTMLElement || sliderElement.closest('.zoom-track') as HTMLElement |
|
|
|
if (!sliderElement) return |
|
|
|
} |
|
|
|
|
|
|
|
const rect = sliderElement.getBoundingClientRect() |
|
|
|
const y = event.clientY - rect.top |
|
|
|
const percentage = Math.max(0, Math.min(100, (y / rect.height) * 100)) |
|
|
|
|
|
|
|
const newZoom = zoomFromPosition(percentage) |
|
|
|
if (window.wayline) { |
|
|
|
const minimapViewer = window.wayline.minimapViewer |
|
|
|
if (minimapViewer) { |
|
|
|
function fovFromZoom(zoom: number, fov1: number = 1.3418): number { |
|
|
|
const k = Math.tan(fov1 / 2) |
|
|
|
return 2 * Math.atan(k / zoom) |
|
|
|
} |
|
|
|
const currentFov = fovFromZoom(newZoom) |
|
|
|
store.updateZoomFactor(newZoom) |
|
|
|
minimapViewer.camera.frustum.fov = currentFov |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function toggleMinimapExpand() { |
|
|
|
isMinimapExpanded.value = !isMinimapExpanded.value |
|
|
|
} |
|
|
|
|
|
|
|
let viewer: Viewer |
|
|
|
Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4NDU1Zjk5ZS0xOWQzLTQ2ZDEtYTk5MC03NmFlMGIwYTE4YzIiLCJpZCI6MTMwMTIsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1NjIzMTc2OTh9.1gS4dOGsPgIB_Xd5ERV_LPBPrj12oVUX4ZtyQyoYw58' |
|
|
|
@ -67,19 +203,86 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
|
|
|
|
<template> |
|
|
|
<div id="cesium-viewer" ref="viewerDivRef"></div> |
|
|
|
<div id="minimap"> |
|
|
|
<div class="zoom-frame warning" style="width: 200px; height: 150px;"> |
|
|
|
<div class="top left corner"></div> |
|
|
|
<div class="top right corner"></div> |
|
|
|
<div class="bottom left corner"></div> |
|
|
|
<div class="bottom right corner"></div><span class="frame-title map-text-shadow warning"> |
|
|
|
{{ store.zoomFactor }}X |
|
|
|
<span class="uranus-icon uranus-icon-cursor-pointer warning-tooltips warning-tooltips warning-tooltips"> |
|
|
|
<svg class="svgfont svgfont-warning_outline" style="width: 1em; height: 1em;"> |
|
|
|
<use xlink:href="#warning_outline"></use> |
|
|
|
</svg> |
|
|
|
<div id="minimap" :class="{ 'expanded': isMinimapExpanded }"> |
|
|
|
<!-- 小框放大按钮 --> |
|
|
|
<button class="minimap-expand-btn" @click="toggleMinimapExpand" :title="isMinimapExpanded ? '缩小' : '放大'"> |
|
|
|
<svg v-if="!isMinimapExpanded" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|
|
|
<path d="M2 2H6V3H3V6H2V2ZM10 2H14V6H13V3H10V2ZM2 10V14H6V13H3V10H2ZM13 10V13H10V14H14V10H13Z" fill="white"/> |
|
|
|
</svg> |
|
|
|
<svg v-else width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|
|
|
<path d="M11 5H13V7H11V5ZM5 5H7V7H5V5ZM11 9H13V11H11V9ZM5 9H7V11H5V9Z" fill="white"/> |
|
|
|
</svg> |
|
|
|
</button> |
|
|
|
|
|
|
|
<!-- Minimap HUD 覆盖层(按截图布局) --> |
|
|
|
<div class="minimap-hud"> |
|
|
|
<!-- 中间:对焦框(zoom-frame) --> |
|
|
|
<div class="zoom-frame warning"> |
|
|
|
<div class="top left corner"></div> |
|
|
|
<div class="top right corner"></div> |
|
|
|
<div class="bottom left corner"></div> |
|
|
|
<div class="bottom right corner"></div> |
|
|
|
<span class="frame-title map-text-shadow warning"> |
|
|
|
{{ store.zoomFactor }}X |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 顶部居中:镜头切换 --> |
|
|
|
<div class="minimap-lens-controls"> |
|
|
|
<button |
|
|
|
:class="['minimap-lens-btn', { 'active': store.lensMode === 'wide' }]" |
|
|
|
@click="store.setLensMode('wide')" |
|
|
|
> |
|
|
|
广角 {{ store.lensMode === 'wide' ? store.zoomFactor : 1 }}X |
|
|
|
</button> |
|
|
|
<button |
|
|
|
:class="['minimap-lens-btn', { 'active': store.lensMode === 'zoom' }]" |
|
|
|
@click="store.setLensMode('zoom')" |
|
|
|
> |
|
|
|
变焦 {{ store.lensMode === 'zoom' ? store.zoomFactor : 1 }}X |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 中上:偏航角 --> |
|
|
|
<div class="minimap-yaw"> |
|
|
|
偏航角 {{ Math.round(store.yawAngle) }} |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 左侧:云台 --> |
|
|
|
<div class="minimap-gimbal"> |
|
|
|
<div class="minimap-gimbal-title">云台</div> |
|
|
|
<div class="minimap-gimbal-value">{{ Math.round(store.gimbalPitch) }}</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 底部居中:保存 --> |
|
|
|
<button class="minimap-save-btn" type="button"> |
|
|
|
保存 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="minimap-zoom-slider" :class="{ 'disabled': store.lensMode === 'wide' }"> |
|
|
|
<div class="minimap-zoom-marks"> |
|
|
|
<span class="minimap-zoom-mark">56x</span> |
|
|
|
<span class="minimap-zoom-mark">14x</span> |
|
|
|
<span class="minimap-zoom-mark">7x</span> |
|
|
|
<span class="minimap-zoom-mark">2x</span> |
|
|
|
<span class="minimap-zoom-mark">1x</span> |
|
|
|
</div> |
|
|
|
<div |
|
|
|
class="minimap-zoom-track" |
|
|
|
:class="{ 'disabled': store.lensMode === 'wide' }" |
|
|
|
@mousedown="onSliderMouseDown" |
|
|
|
@mousemove="onSliderMouseMove" |
|
|
|
@mouseup="onSliderMouseUp" |
|
|
|
@mouseleave="onSliderMouseUp" |
|
|
|
> |
|
|
|
<div |
|
|
|
class="minimap-zoom-handle" |
|
|
|
:class="{ 'disabled': store.lensMode === 'wide' }" |
|
|
|
:style="{ bottom: getSliderPosition() + '%' }" |
|
|
|
@mousedown.stop="onHandleMouseDown" |
|
|
|
></div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div style="position: fixed; bottom: 20px; left: 200px;"> |
|
|
|
@ -87,6 +290,49 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
<img id="compass" src="/compass.svg" draggable="false"> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="point-list-container"> |
|
|
|
<div class="point-list-header">航点列表 ({{ store.pointList.length }})</div> |
|
|
|
<div class="point-list-content"> |
|
|
|
<div |
|
|
|
v-for="point in store.pointList" |
|
|
|
:key="point.index" |
|
|
|
:class="['point-item', { 'selected': point.selected }]" |
|
|
|
> |
|
|
|
<div class="point-index">#{{ point.index + 1 }}</div> |
|
|
|
<div class="point-info"> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">经度:</span> |
|
|
|
<span class="value">{{ point.longitude.toFixed(6) }}°</span> |
|
|
|
</div> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">纬度:</span> |
|
|
|
<span class="value">{{ point.latitude.toFixed(6) }}°</span> |
|
|
|
</div> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">ASL:</span> |
|
|
|
<span class="value">{{ point.asl.toFixed(1) }} m</span> |
|
|
|
</div> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">HAE:</span> |
|
|
|
<span class="value">{{ point.alt.toFixed(1) }} m</span> |
|
|
|
</div> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">航向:</span> |
|
|
|
<span class="value">{{ point.heading.toFixed(1) }}°</span> |
|
|
|
</div> |
|
|
|
<div class="point-row"> |
|
|
|
<span class="label">俯仰:</span> |
|
|
|
<span class="value">{{ point.pitch.toFixed(1) }}°</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-if="store.pointList.length === 0" class="empty-message"> |
|
|
|
暂无航点,按空格键创建航点 |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
@ -101,6 +347,33 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
position: fixed; |
|
|
|
bottom: 20px; |
|
|
|
right: 20px; |
|
|
|
background: rgba(0, 0, 0, 0.25); |
|
|
|
border: 2px solid rgba(255, 255, 255, 0.65); |
|
|
|
border-radius: 10px; |
|
|
|
transition: all 0.3s ease; |
|
|
|
z-index: 1000; |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
|
|
|
|
#minimap::before { |
|
|
|
content: ""; |
|
|
|
position: absolute; |
|
|
|
inset: 0; |
|
|
|
background: rgba(0, 0, 0, 0.22); |
|
|
|
pointer-events: none; |
|
|
|
z-index: 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* Cesium minimap canvas fill */ |
|
|
|
#minimap :deep(.cesium-viewer) { |
|
|
|
position: absolute; |
|
|
|
inset: 0; |
|
|
|
z-index: 0; |
|
|
|
} |
|
|
|
|
|
|
|
#minimap.expanded { |
|
|
|
width: 600px; |
|
|
|
height: 450px; |
|
|
|
} |
|
|
|
|
|
|
|
._dji_compass_p0y8b_78 { |
|
|
|
@ -108,9 +381,6 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
} |
|
|
|
|
|
|
|
.zoom-frame { |
|
|
|
position: fixed; |
|
|
|
right: 20px; |
|
|
|
bottom: 20px; |
|
|
|
position: absolute; |
|
|
|
top: 50%; |
|
|
|
left: 50%; |
|
|
|
@ -118,6 +388,8 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
z-index: 999; |
|
|
|
user-select: none; |
|
|
|
pointer-events: none; |
|
|
|
width: 200px; |
|
|
|
height: 150px; |
|
|
|
} |
|
|
|
|
|
|
|
.zoom-frame .corner { |
|
|
|
@ -152,10 +424,12 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
} |
|
|
|
|
|
|
|
.zoom-frame .frame-title { |
|
|
|
color: #00ee8b; |
|
|
|
bottom: -8px; |
|
|
|
right: 0; |
|
|
|
transform: translateY(100%); |
|
|
|
color: #fff; |
|
|
|
top: -10px; |
|
|
|
left: 50%; |
|
|
|
transform: translate(-50%, -100%); |
|
|
|
font-weight: 700; |
|
|
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6); |
|
|
|
} |
|
|
|
|
|
|
|
.frame-title { |
|
|
|
@ -180,4 +454,354 @@ async function getTerrainPosition(cartographic: Cartographic) { |
|
|
|
transform: translate(-50%, -100%); |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
.point-list-container { |
|
|
|
position: fixed; |
|
|
|
top: 20px; |
|
|
|
left: 20px; |
|
|
|
width: 300px; |
|
|
|
max-height: calc(100vh - 40px); |
|
|
|
background: rgba(0, 0, 0, 0.8); |
|
|
|
border: 2px solid #00D690; |
|
|
|
border-radius: 8px; |
|
|
|
color: white; |
|
|
|
font-family: sans-serif; |
|
|
|
z-index: 1000; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
} |
|
|
|
|
|
|
|
.point-list-header { |
|
|
|
padding: 12px 16px; |
|
|
|
background: rgba(0, 214, 144, 0.2); |
|
|
|
border-bottom: 1px solid #00D690; |
|
|
|
font-weight: 600; |
|
|
|
font-size: 16px; |
|
|
|
} |
|
|
|
|
|
|
|
.point-list-content { |
|
|
|
overflow-y: auto; |
|
|
|
padding: 8px; |
|
|
|
max-height: calc(100vh - 100px); |
|
|
|
} |
|
|
|
|
|
|
|
.point-item { |
|
|
|
background: rgba(255, 255, 255, 0.05); |
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
|
|
border-radius: 6px; |
|
|
|
padding: 12px; |
|
|
|
margin-bottom: 8px; |
|
|
|
transition: all 0.3s ease; |
|
|
|
} |
|
|
|
|
|
|
|
.point-item:hover { |
|
|
|
background: rgba(255, 255, 255, 0.1); |
|
|
|
border-color: rgba(0, 214, 144, 0.5); |
|
|
|
} |
|
|
|
|
|
|
|
.point-item.selected { |
|
|
|
background: rgba(0, 214, 144, 0.2); |
|
|
|
border-color: #00D690; |
|
|
|
box-shadow: 0 0 10px rgba(0, 214, 144, 0.3); |
|
|
|
} |
|
|
|
|
|
|
|
.point-index { |
|
|
|
font-weight: 600; |
|
|
|
font-size: 18px; |
|
|
|
color: #00D690; |
|
|
|
margin-bottom: 8px; |
|
|
|
padding-bottom: 8px; |
|
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.point-info { |
|
|
|
font-size: 12px; |
|
|
|
} |
|
|
|
|
|
|
|
.point-row { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
margin-bottom: 4px; |
|
|
|
} |
|
|
|
|
|
|
|
.point-row .label { |
|
|
|
color: rgba(255, 255, 255, 0.7); |
|
|
|
} |
|
|
|
|
|
|
|
.point-row .value { |
|
|
|
color: white; |
|
|
|
font-weight: 500; |
|
|
|
} |
|
|
|
|
|
|
|
.empty-message { |
|
|
|
text-align: center; |
|
|
|
padding: 40px 20px; |
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
font-size: 14px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 顶部中央:镜头模式切换 */ |
|
|
|
/* 左上角:放大/缩小按钮(按截图) */ |
|
|
|
.minimap-expand-btn { |
|
|
|
position: absolute; |
|
|
|
top: 10px; |
|
|
|
left: 10px; |
|
|
|
width: 26px; |
|
|
|
height: 26px; |
|
|
|
background: rgba(0, 0, 0, 0.55); |
|
|
|
border: 1px solid rgba(255, 255, 255, 0.35); |
|
|
|
border-radius: 6px; |
|
|
|
color: white; |
|
|
|
cursor: pointer; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
transition: all 0.2s ease; |
|
|
|
z-index: 3; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-expand-btn:hover { |
|
|
|
background: rgba(0, 0, 0, 0.75); |
|
|
|
border-color: rgba(255, 255, 255, 0.6); |
|
|
|
} |
|
|
|
|
|
|
|
/* HUD 覆盖层容器 */ |
|
|
|
.minimap-hud { |
|
|
|
position: absolute; |
|
|
|
inset: 0; |
|
|
|
z-index: 2; |
|
|
|
pointer-events: none; |
|
|
|
} |
|
|
|
|
|
|
|
/* 顶部居中:镜头切换(按截图两段按钮) */ |
|
|
|
.minimap-lens-controls { |
|
|
|
position: absolute; |
|
|
|
top: 14px; |
|
|
|
left: 50%; |
|
|
|
transform: translateX(-50%); |
|
|
|
display: flex; |
|
|
|
gap: 0; |
|
|
|
background: rgba(0, 0, 0, 0.45); |
|
|
|
border-radius: 6px; |
|
|
|
padding: 4px; |
|
|
|
pointer-events: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-lens-btn { |
|
|
|
padding: 10px 22px; |
|
|
|
background: rgba(255, 255, 255, 0.10); |
|
|
|
border: none; |
|
|
|
border-radius: 4px; |
|
|
|
color: rgba(255, 255, 255, 0.70); |
|
|
|
font-size: 14px; |
|
|
|
font-weight: 600; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.2s ease; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-lens-btn:first-child { |
|
|
|
border-top-right-radius: 0; |
|
|
|
border-bottom-right-radius: 0; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-lens-btn:last-child { |
|
|
|
border-top-left-radius: 0; |
|
|
|
border-bottom-left-radius: 0; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-lens-btn.active { |
|
|
|
background: #2F80ED; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-lens-btn:not(.active):hover { |
|
|
|
background: rgba(255, 255, 255, 0.15); |
|
|
|
color: rgba(255, 255, 255, 0.8); |
|
|
|
} |
|
|
|
|
|
|
|
/* 中上:偏航角 */ |
|
|
|
.minimap-yaw { |
|
|
|
position: absolute; |
|
|
|
top: 72px; |
|
|
|
left: 50%; |
|
|
|
transform: translateX(-50%); |
|
|
|
color: rgba(255, 255, 255, 0.92); |
|
|
|
font-size: 18px; |
|
|
|
font-weight: 700; |
|
|
|
letter-spacing: 1px; |
|
|
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6); |
|
|
|
} |
|
|
|
|
|
|
|
/* 左侧:云台 */ |
|
|
|
.minimap-gimbal { |
|
|
|
position: absolute; |
|
|
|
top: 120px; |
|
|
|
left: 48px; |
|
|
|
color: rgba(255, 255, 255, 0.92); |
|
|
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6); |
|
|
|
font-weight: 700; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-gimbal-title { |
|
|
|
font-size: 16px; |
|
|
|
margin-bottom: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-gimbal-value { |
|
|
|
font-size: 22px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 底部:保存按钮 */ |
|
|
|
.minimap-save-btn { |
|
|
|
position: absolute; |
|
|
|
left: 50%; |
|
|
|
bottom: 18px; |
|
|
|
transform: translateX(-50%); |
|
|
|
width: 160px; |
|
|
|
height: 44px; |
|
|
|
border: none; |
|
|
|
border-radius: 8px; |
|
|
|
background: #2F80ED; |
|
|
|
color: #fff; |
|
|
|
font-size: 16px; |
|
|
|
font-weight: 700; |
|
|
|
cursor: pointer; |
|
|
|
pointer-events: auto; |
|
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35); |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-save-btn:hover { |
|
|
|
filter: brightness(1.05); |
|
|
|
} |
|
|
|
|
|
|
|
/* Minimap内的变焦滑块(右侧竖条) */ |
|
|
|
.minimap-zoom-slider { |
|
|
|
position: absolute; |
|
|
|
top: 50%; |
|
|
|
right: 16px; |
|
|
|
transform: translateY(-50%); |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
z-index: 3; |
|
|
|
pointer-events: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-marks { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
justify-content: space-between; |
|
|
|
height: 180px; |
|
|
|
color: rgba(255, 255, 255, 0.9); |
|
|
|
font-size: 12px; |
|
|
|
font-weight: 600; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-mark { |
|
|
|
text-align: right; |
|
|
|
min-width: 25px; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-track { |
|
|
|
position: relative; |
|
|
|
width: 6px; |
|
|
|
height: 180px; |
|
|
|
background: rgba(255, 255, 255, 0.35); |
|
|
|
border-radius: 10px; |
|
|
|
cursor: pointer; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-handle { |
|
|
|
position: absolute; |
|
|
|
left: 50%; |
|
|
|
transform: translateX(-50%); |
|
|
|
width: 18px; |
|
|
|
height: 18px; |
|
|
|
background: white; |
|
|
|
border-radius: 50%; |
|
|
|
cursor: grab; |
|
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); |
|
|
|
transition: box-shadow 0.2s; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-handle:active { |
|
|
|
cursor: grabbing; |
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-slider.disabled .minimap-zoom-track, |
|
|
|
.minimap-zoom-track.disabled { |
|
|
|
opacity: 0.4; |
|
|
|
cursor: not-allowed; |
|
|
|
pointer-events: none; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-zoom-handle.disabled { |
|
|
|
opacity: 0.4; |
|
|
|
cursor: not-allowed; |
|
|
|
pointer-events: none; |
|
|
|
} |
|
|
|
|
|
|
|
/* 小框放大按钮 */ |
|
|
|
.minimap-expand-btn { |
|
|
|
position: absolute; |
|
|
|
top: 8px; |
|
|
|
right: 8px; |
|
|
|
width: 24px; |
|
|
|
height: 24px; |
|
|
|
background: rgba(0, 0, 0, 0.6); |
|
|
|
border: 1px solid rgba(255, 255, 255, 0.3); |
|
|
|
border-radius: 4px; |
|
|
|
color: white; |
|
|
|
cursor: pointer; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
transition: all 0.2s ease; |
|
|
|
z-index: 1001; |
|
|
|
} |
|
|
|
|
|
|
|
.minimap-expand-btn:hover { |
|
|
|
background: rgba(0, 0, 0, 0.8); |
|
|
|
border-color: rgba(255, 255, 255, 0.5); |
|
|
|
} |
|
|
|
|
|
|
|
/* 云台和偏航角数据显示 */ |
|
|
|
.gimbal-data-display { |
|
|
|
position: fixed; |
|
|
|
top: 80px; |
|
|
|
left: 50%; |
|
|
|
transform: translateX(-50%); |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
gap: 8px; |
|
|
|
z-index: 1000; |
|
|
|
background: rgba(0, 0, 0, 0.6); |
|
|
|
padding: 12px 20px; |
|
|
|
border-radius: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
.data-line { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
gap: 16px; |
|
|
|
color: white; |
|
|
|
font-size: 14px; |
|
|
|
} |
|
|
|
|
|
|
|
.data-label { |
|
|
|
color: rgba(255, 255, 255, 0.8); |
|
|
|
font-weight: 500; |
|
|
|
} |
|
|
|
|
|
|
|
.data-value { |
|
|
|
color: white; |
|
|
|
font-weight: 600; |
|
|
|
font-size: 16px; |
|
|
|
min-width: 50px; |
|
|
|
text-align: right; |
|
|
|
} |
|
|
|
</style> |