FileManage/web/src/components/trajectory/index.vue

140 lines
3.1 KiB
Vue
Raw Normal View History

2025-03-19 09:17:05 +08:00
<template>
<div ref="mapContainer" class="map-container"></div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import * as d3 from 'd3'
import chinaData from './newJson.json'
const props = defineProps({
coordinates: {
type: Array,
default: () => [
[116.405285, 39.904989], // 北京
[121.472644, 31.231706] // 上海
]
}
})
const mapContainer = ref(null)
let svg, mapGroup, markersGroup, zoom
const initMap = () => {
const width = 1000
const height = 800
const projection = d3.geoMercator()
.center([104, 37])
.scale(600)
.translate([width / 2, height / 2])
const path = d3.geoPath().projection(projection)
if (svg) svg.selectAll("*").remove()
svg = d3.select(mapContainer.value)
.append('svg')
.attr('width', width)
.attr('height', height)
// 修改分组结构
const mainGroup = svg.append('g')
mapGroup = mainGroup.append('g')
markersGroup = mainGroup.append('g') // 标记层放入主缩放组
mapGroup.append('g')
.selectAll('path')
.data(chinaData.features)
.enter()
.append('path')
.attr('d', path)
.attr('fill', '#e7e7e7')
.attr('stroke', '#fff')
zoom = d3.zoom()
.scaleExtent([0.2, 8])
.on('zoom', (event) => {
mainGroup.attr('transform', event.transform)
})
svg.call(zoom)
}
const updateRoute = () => {
const projection = d3.geoMercator()
.center([104, 37])
.scale(600)
.translate([1000 / 2, 800 / 2])
mapGroup.selectAll('.route-path').remove()
markersGroup.selectAll('*').remove()
const routeData = {
type: "LineString",
coordinates: props.coordinates
}
mapGroup.append('path')
.datum(routeData)
.attr('class', 'route-path')
.attr('d', d3.geoPath().projection(projection))
.attr('fill', 'none')
.attr('stroke', '#f00')
.attr('stroke-width', 2)
.attr('stroke-dasharray', function() {
return this.getTotalLength()
})
.attr('stroke-dashoffset', function() {
return this.getTotalLength()
})
.transition()
.duration(2000)
.attr('stroke-dashoffset', 0)
// 修改标记点绘制逻辑
props.coordinates.forEach((coord, i) => {
if (!Array.isArray(coord) || coord.length !== 2) return
const lon = Number(coord[0])
const lat = Number(coord[1])
if (isNaN(lon) || isNaN(lat)) return
const [x, y] = projection([lon, lat])
// 添加缩放补偿逻辑
const currentTransform = d3.zoomTransform(svg.node())
const baseRadius = 8
markersGroup.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', baseRadius / currentTransform.k) // 根据缩放级别调整半径
.attr('fill', '#ff4757')
.attr('stroke', 'white')
.attr('stroke-width', 2 / currentTransform.k) // 同步调整描边宽度
})
}
onMounted(() => {
if(props.coordinates){
initMap()
updateRoute()
}
})
watch(() => props.coordinates, () => {
updateRoute()
}, { deep: true })
</script>
<style>
/* 保持原有样式不变 */
.map-container {
background-color: #f0f8ff;
margin: 20px;
overflow: hidden;
touch-action: none;
}
</style>