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

167 lines
3.9 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>
2025-03-20 18:31:12 +08:00
import { ref, onMounted, watch,defineEmits } from 'vue'
2025-03-19 09:17:05 +08:00
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] // 上海
]
2025-03-20 18:31:12 +08:00
},
qvehuan: {
type: Boolean,
default: () => false
2025-03-19 09:17:05 +08:00
}
})
2025-03-20 18:31:12 +08:00
const emit = defineEmits(['qvehuan1']);
2025-03-19 09:17:05 +08:00
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')
2025-03-20 18:31:12 +08:00
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') // 防止干扰交互
2025-03-19 09:17:05 +08:00
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])
2025-03-20 18:31:12 +08:00
if (initqie.value) {
// debugger
mapGroup.selectAll('.route-path').remove()
markersGroup.selectAll('*').remove()
initqie.value = false
emit('qvehuan1', initqie.value);
}
2025-03-19 09:17:05 +08:00
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')
2025-03-20 18:31:12 +08:00
.attr('stroke-width', 1)
.attr('stroke-dasharray', function () {
2025-03-19 09:17:05 +08:00
return this.getTotalLength()
})
2025-03-20 18:31:12 +08:00
.attr('stroke-dashoffset', function () {
2025-03-19 09:17:05 +08:00
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])
2025-03-20 18:31:12 +08:00
2025-03-19 09:17:05 +08:00
if (isNaN(lon) || isNaN(lat)) return
const [x, y] = projection([lon, lat])
2025-03-20 18:31:12 +08:00
// 添加缩放补偿逻
2025-03-19 09:17:05 +08:00
markersGroup.append('circle')
.attr('cx', x)
.attr('cy', y)
2025-03-20 18:31:12 +08:00
.attr('r', 1) // 根据缩放级别调整半径
2025-03-19 09:17:05 +08:00
.attr('fill', '#ff4757')
.attr('stroke', 'white')
2025-03-20 18:31:12 +08:00
.attr('stroke-width', 0.1) // 同步调整描边宽度
2025-03-19 09:17:05 +08:00
})
}
2025-03-20 18:31:12 +08:00
const initqie = ref(false)
2025-03-19 09:17:05 +08:00
onMounted(() => {
2025-03-20 18:31:12 +08:00
if (props.coordinates) {
2025-03-19 09:17:05 +08:00
initMap()
updateRoute()
}
2025-03-20 18:31:12 +08:00
})
watch(() => props.qvehuan, (newVal) => {
initqie.value = newVal
updateRoute()
}, { deep: true })
watch(() => props.coordinates, (newVal) => {
2025-03-19 09:17:05 +08:00
updateRoute()
}, { deep: true })
</script>
<style>
/* 保持原有样式不变 */
.map-container {
background-color: #f0f8ff;
margin: 20px;
overflow: hidden;
touch-action: none;
}
</style>