FileManage/web/src/components/trajectory/index.vue
2025-03-20 18:31:12 +08:00

167 lines
3.9 KiB
Vue

<template>
<div ref="mapContainer" class="map-container"></div>
</template>
<script setup>
import { ref, onMounted, watch,defineEmits } 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] // 上海
]
},
qvehuan: {
type: Boolean,
default: () => false
}
})
const emit = defineEmits(['qvehuan1']);
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')
mapGroup.append('g')
.selectAll('text')
.data(chinaData.features)
.enter()
.append('text')
.attr('x', d => {
const centroid = path.centroid(d)
return centroid[0]
})
.attr('y', d => {
const centroid = path.centroid(d)
return centroid[1]
})
.text(d => d.properties.name || '') // 确保JSON数据包含properties.name
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.attr('fill', '#333')
.style('font-size', '12px')
.style('font-family', 'Arial')
.style('pointer-events', 'none') // 防止干扰交互
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])
if (initqie.value) {
// debugger
mapGroup.selectAll('.route-path').remove()
markersGroup.selectAll('*').remove()
initqie.value = false
emit('qvehuan1', initqie.value);
}
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', 1)
.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])
// 添加缩放补偿逻
markersGroup.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', 1) // 根据缩放级别调整半径
.attr('fill', '#ff4757')
.attr('stroke', 'white')
.attr('stroke-width', 0.1) // 同步调整描边宽度
})
}
const initqie = ref(false)
onMounted(() => {
if (props.coordinates) {
initMap()
updateRoute()
}
})
watch(() => props.qvehuan, (newVal) => {
initqie.value = newVal
updateRoute()
}, { deep: true })
watch(() => props.coordinates, (newVal) => {
updateRoute()
}, { deep: true })
</script>
<style>
/* 保持原有样式不变 */
.map-container {
background-color: #f0f8ff;
margin: 20px;
overflow: hidden;
touch-action: none;
}
</style>