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>
|