吉安感知网项目-前端
chenyao
3 days ago 43e1f2d3af6b23413fc92773929bffc680bb54da
feat:优化历史轨迹
2 files modified
235 ■■■■ changed files
applications/drone-command/src/components/map-container/common-cesium-map.vue 202 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue 33 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/common-cesium-map.vue
@@ -5,23 +5,36 @@
                <div class="timeline-prefix">
                    <slot name="timeline-prefix"></slot>
                </div>
                <div class="card-content">
                    <div class="card-item" v-for="(item, index) in props.list" @click="clickWeekTime(item, index)">
                        <div class="time-point">
                            <img
                                class="active"
                                v-if="index === selectedImgIndex"
                                src="@/assets/images/common/point-active.png"
                                alt=""
                            />
                            <img v-else src="@/assets/images/common/point.png" alt="" />
                <div
                    ref="timelineRef"
                    class="card-content"
                    :class="{ 'is-dragging': timelineDragging }"
                    @mousedown="handleTimelineMouseDown"
                    @mousemove="handleTimelineMouseMove"
                    @mouseup="stopTimelineDrag"
                    @mouseleave="stopTimelineDrag"
                >
                    <div class="timeline-track">
                        <div class="time-line"></div>
                        <div
                            class="card-item"
                            v-for="(item, index) in props.list"
                            @click="handleTimelineItemClick(item, index)"
                        >
                            <div class="time-point">
                                <img
                                    class="active"
                                    v-if="index === selectedImgIndex"
                                    src="@/assets/images/common/point-active.png"
                                    alt=""
                                />
                                <img v-else src="@/assets/images/common/point.png" alt="" />
                            </div>
                            <div class="time-bottom">{{ item.createTime.slice(11) }}</div>
                        </div>
                        <div class="time-bottom">{{ item.createTime.slice(11) }}</div>
                    </div>
                </div>
            </div>
            <div class="time-line"></div>
        </template>
    </div>
</template>
@@ -91,6 +104,11 @@
const emit = defineEmits(['ready', 'height-change', 'stage-change', 'point-change'])
const mapId = props.domId || `common-cesium-map-${Math.random().toString(36).slice(2, 10)}`
const timelineRef = ref(null)
const timelineDragging = ref(false)
let timelineStartX = 0
let timelineStartScrollLeft = 0
let timelineDidDrag = false
let viewInstance = null
let viewer = null
const ADMIN_BOUNDARY_NAMES = {
@@ -144,19 +162,89 @@
const selectedImgIndex = ref(0)
// 航线高度(与 TrajectoryDiaLog 里 MOCK_FLIGHT_HEIGHT 保持一致),让标记悬浮在轨迹线上
const FLIGHT_HEIGHT = 120
const POSITION_MOVE_DURATION = 1000
// 当前经纬度位置标记
let positionEntity = null
let positionAnimationFrame = null
// 停止位置图标移动动画
const stopPositionAnimation = () => {
    if (positionAnimationFrame !== null) {
        cancelAnimationFrame(positionAnimationFrame)
        positionAnimationFrame = null
    }
}
// 平滑移动位置图标
const animatePositionMarker = (targetPosition, duration) => {
    const currentPosition = positionEntity?.position?.getValue(viewer.clock.currentTime, new Cesium.Cartesian3())
    if (!currentPosition) {
        positionEntity.position = targetPosition
        return
    }
    stopPositionAnimation()
    const startTime = performance.now()
    const nextPosition = new Cesium.Cartesian3()
    // 更新位置图标移动进度
    const updatePosition = currentTime => {
        const progress = Math.min((currentTime - startTime) / duration, 1)
        Cesium.Cartesian3.lerp(currentPosition, targetPosition, progress, nextPosition)
        positionEntity.position.setValue(nextPosition)
        viewer.scene.requestRender()
        if (progress < 1) {
            positionAnimationFrame = requestAnimationFrame(updatePosition)
        } else {
            positionAnimationFrame = null
        }
    }
    positionAnimationFrame = requestAnimationFrame(updatePosition)
}
// 点击周期
const clickWeekTime = (item, index) => {
const clickWeekTime = (item, index, options = {}) => {
    // clickImgSrc.value = item.url
    selectedImgIndex.value = index
    showPositionMarker(item)
    showPositionMarker(item, options)
    // 通知父组件当前选中的轨迹点,用于右侧表格同步高亮
    emit('point-change', { item, index })
}
// 开始拖动时间轴
const handleTimelineMouseDown = event => {
    if (event.button !== 0) return
    timelineDragging.value = true
    timelineDidDrag = false
    timelineStartX = event.clientX
    timelineStartScrollLeft = timelineRef.value?.scrollLeft ?? 0
}
// 横向拖动时间轴
const handleTimelineMouseMove = event => {
    if (!timelineDragging.value || !timelineRef.value) return
    const offsetX = event.clientX - timelineStartX
    if (Math.abs(offsetX) > 3) timelineDidDrag = true
    if (!timelineDidDrag) return
    timelineRef.value.scrollLeft = timelineStartScrollLeft - offsetX
    event.preventDefault()
}
// 停止拖动时间轴
const stopTimelineDrag = () => {
    timelineDragging.value = false
    setTimeout(() => {
        timelineDidDrag = false
    }, 0)
}
// 点击时间轴节点
const handleTimelineItemClick = (item, index) => {
    if (timelineDidDrag) return
    clickWeekTime(item, index)
}
// 在地图上通过经纬度显示位置图标
const showPositionMarker = item => {
const showPositionMarker = (item, { animate = false, duration = POSITION_MOVE_DURATION } = {}) => {
    if (!viewer) return
    const longitude = Number(item?.longitude)
    const latitude = Number(item?.latitude)
@@ -165,8 +253,12 @@
    // 与航线相同的高度,让标记悬浮在轨迹线上,而不是贴地
    const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, FLIGHT_HEIGHT)
    if (positionEntity) {
        // 已存在则移动到新的位置
        positionEntity.position = position
        if (animate) {
            animatePositionMarker(position, duration)
        } else {
            stopPositionAnimation()
            positionEntity.position = position
        }
    } else {
        positionEntity = viewer.entities.add({
            position,
@@ -212,6 +304,7 @@
})
onBeforeUnmount(() => {
    stopPositionAnimation()
    if (removeCameraListener) removeCameraListener()
    removeJaAdminBoundary(viewer, adminBoundarySources)
    adminBoundarySources = null
@@ -320,11 +413,9 @@
        bottom: 0px;
        width: 100%;
        height: 67px;
        box-sizing: border-box;
        background: rgba(0, 0, 0, 0.42);
        border-radius: 0px 0px 20px 20px;
        padding-left: 6px;
        // padding-right: 6px;
        // border: 1px solid red;
        border-radius: 0;
        color: #d5d5d5;
        font-size: 14px;
@@ -342,20 +433,43 @@
        .card-content {
            display: flex;
            justify-content: center;
            position: absolute;
            width: calc(100% - 6px - 6px);
            left: 15%;
            width: 70%;
            height: 100%;
            overflow-x: scroll;
            overflow-x: auto;
            overflow-y: hidden;
            white-space: nowrap;
            cursor: grab;
            scrollbar-width: none;
            &::-webkit-scrollbar {
                display: none;
            }
            &.is-dragging {
                cursor: grabbing;
                user-select: none;
            }
            .timeline-track {
                position: relative;
                display: flex;
                flex: 0 0 auto;
                height: 100%;
                margin: 0 auto;
            }
            .card-item {
                position: relative;
                flex: 0 0 82px;
                width: 82px;
                text-align: center;
                cursor: pointer;
            }
            & > div {
                position: relative;
            &.is-dragging .card-item {
                cursor: grabbing;
            }
            .time-top {
@@ -394,36 +508,20 @@
            }
            .time-bottom {
                // border: 1px solid red;
                margin-top: 44px;
                margin-left: 8px;
                &:first-child {
                    margin-left: 0;
                }
            }
            // .time-line {
            //     // position: absolute;
            //     // top: 35px;
            //     // width: 100%;
            //     position: fixed;
            //     bottom: 96px;
            //     width: 94%;
            //     border: 1px solid rgba(237, 237, 237, 0.6);
            //     z-index: 0;
            // }
        }
    }
    .time-line {
            .time-line {
                position: absolute;
                // top: 35px;
                width: 70%;
                // position: fixed;
        left: 15%;
                left: 41px;
                right: 41px;
                bottom: 36px;
                border: 1px solid rgba(237, 237, 237, 0.6);
                height: 1px;
                background: rgba(237, 237, 237, 0.6);
                z-index: 0;
                pointer-events: none;
            }
        }
    }
}
</style>
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
@@ -195,7 +195,7 @@
// 停止轨迹播放
function stopTrackPlay() {
    if (playTimer) {
        clearInterval(playTimer)
        clearTimeout(playTimer)
        playTimer = null
    }
    isPlaying.value = false
@@ -207,9 +207,19 @@
        stopTrackPlay()
        return
    }
    mapRef.value?.clickWeekTime?.(list.value[playIndex], playIndex)
    mapRef.value?.clickWeekTime?.(list.value[playIndex], playIndex, {
        animate: true,
        duration: PLAY_INTERVAL,
    })
    playIndex += 1
    if (playIndex >= list.value.length) stopTrackPlay()
    playTimer = setTimeout(() => {
        playTimer = null
        if (playIndex >= list.value.length) {
            stopTrackPlay()
            return
        }
        playNextPoint()
    }, PLAY_INTERVAL)
}
// 切换轨迹播放状态
@@ -219,10 +229,11 @@
        return
    }
    if (!list.value.length) return
    playIndex = 0
    mapRef.value?.clickWeekTime?.(list.value[0], 0)
    if (list.value.length === 1) return
    playIndex = 1
    isPlaying.value = true
    playNextPoint()
    if (isPlaying.value) playTimer = setInterval(playNextPoint, PLAY_INTERVAL)
}
// 地图上点击轨迹点后,同步高亮表格对应行,并滚动到该行
@@ -280,6 +291,18 @@
</script>
<style scoped lang="scss">
.left-container {
    min-width: 0;
    overflow: hidden;
}
.right-container {
    position: relative;
    flex-shrink: 0;
    z-index: 1;
    background: #1a1a2a;
}
:deep(.alarm-row) {
    background: #2b2b4c !important;