唯一的区别就是配置项不一样,以及其他坐标计算差异
地图数据来源:https://datav.aliyun.com/portal/school/atlas/area_generator
在线代码:https://codesandbox.io/p/live/99e7cbfd-803a-421a-902f-82a613082f54
初始化差异
## series
echarts.registerMap('china', mapData);
## geo
echarts.registerMap('China', mapData);
高亮方法 highlightCity
dispatchAction
坐标转换:updateCardPosition
注意getCityCenter
的差别
完整代码 series
<template>
<div class="commonData">
<div id="map"></div>
<div class="city-card" :style="{
left: cardPosition.left,
top: cardPosition.top,
visibility: cardPosition.visible ? 'visible' : 'hidden'
}">
<div class='info'>
<div class='sign top'>
<div class='col'>
<p>用户总数</p>
<span>2,283</span>
</div>
<div class='col'>
<p>在线用户</p>
<span>824</span>
</div>
</div>
<div class='sign bom'>
<div class='col'>
<span>今日入职</span>
<span class="num">254</span>
</div>
<div class='col'>
<span>今日离职</span>
<span class="num">12</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import axios from 'axios';
import { onMounted, ref, onUnmounted, reactive } from 'vue';
// 高亮的城市列表
const citiesToHighlight = [
'北京市',
'上海市',
'广东省',
'江苏省',
'浙江省',
];
// 当前高亮的城市索引
let currentCityIndex = 0;
const highlightInterval = 2000; // 2秒切换一次
let highlightTimer = null;
let myChart = null;
const cityListData = ref([]);
const cardPosition = reactive({
left: '0px',
top: '0px',
visible: false
});
const getHtmlDom = (str) => {
let htmlElement = document.querySelector(str);
htmlElement.innerHTML = ''
htmlElement.removeAttribute('_echarts_instance_')
htmlElement.removeAttribute('style')
return htmlElement
}
onMounted(() => {
axios.get('https://d.xzxo.cn/i/100000_full.json').then(res => {
cityListData.value = res.data
initChart(res.data);
});
});
const getCityCenter = (cityName) => {
if (!cityListData.value || !cityListData.value.features) {
console.error('地图数据未加载');
return null;
}
for (let i = 0; i < cityListData.value.features.length; i++) {
const item = cityListData.value.features[i];
if (item.properties && item.properties.name === cityName) {
// 优先使用 center,如果没有则计算 bbox 的中心
if (item.properties.center) {
return item.properties.center;
} else if (item.properties.bbox) {
const bbox = item.properties.bbox;
return [
(bbox[0] + bbox[2]) / 2,
(bbox[1] + bbox[3]) / 2
];
}
}
}
console.warn(`找不到城市 ${cityName} 的坐标`);
return null;
}
onUnmounted(() => {
if (highlightTimer) {
clearInterval(highlightTimer);
}
if (myChart) {
myChart.dispose();
}
});
const initChart = (mapData) => {
let html = getHtmlDom('#map');
// 注册地图数据
echarts.registerMap('china', mapData);
// 初始化图表
myChart = echarts.init(html);
// 配置项
const option = {
backgroundColor: 'transparent', // 透明背景
series: [{
type: 'map',
map: 'china',
roam: false, // 禁用缩放和平移
zoom: 1.5, // 适当放大
center: [104, 35], // 地图中心位置
itemStyle: {
areaColor: '#0D56AA90', // 默认区域颜色-深蓝半透明
borderColor: '#8CD1FF', // 边界线颜色-蓝色
borderWidth: 1.1, // 边界线宽度
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
emphasis: {
itemStyle: {
areaColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1, // 垂直渐变
colorStops: [{
offset: 0,
color: '#33AFFF' // 渐变起始颜色
}, {
offset: 1,
color: '#3D75D6' // 渐变结束颜色
}]
},
borderWidth: 1.5,
shadowColor: 'rgba(51, 175, 255, 0.7)',
shadowBlur: 10
},
label: {
show: false // 不显示区域名称
}
},
label: {
show: false // 默认不显示标签
},
// 南海诸岛样式
// regions: [{
// name: '南海诸岛',
// itemStyle: {
// areaColor: '#0D56AA90',
// borderColor: '#8CD1FF',
// }
// }],
data: [
]
}]
};
myChart.setOption(option);
// 立即执行一次高亮
highlightCity();
// 设置定时轮播高亮
highlightTimer = setInterval(highlightCity, highlightInterval);
// 响应窗口大小变化
window.addEventListener('resize', () => {
myChart.resize();
});
};
// 高亮当前城市并移动到下一个
const highlightCity = () => {
if (!myChart || citiesToHighlight.length === 0) return;
// 获取前一个高亮的城市索引
const previousIndex = (currentCityIndex - 1 + citiesToHighlight.length) % citiesToHighlight.length;
const previousCity = citiesToHighlight[previousIndex];
// 先取消前一个城市的高亮
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
name: previousCity
});
// 当前要高亮的城市
const cityToHighlight = citiesToHighlight[currentCityIndex];
// 高亮当前城市
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
name: cityToHighlight
});
// 更新卡片位置
updateCardPosition(cityToHighlight);
console.log(`当前高亮: ${cityToHighlight}`);
// 更新索引,指向下一个城市
currentCityIndex = (currentCityIndex + 1) % citiesToHighlight.length;
};
const updateCardPosition = (cityName) => {
const coords = getCityCenter(cityName);
if (!coords || !myChart) {
cardPosition.visible = false;
return;
}
console.log(`城市 ${cityName} 的地理坐标:`, coords);
// 将地理坐标转换为屏幕像素坐标
const pixelPos = convertGeoToPixel(coords);
// 设置卡片位置,配合左右偏移
cardPosition.left = `${pixelPos.x - 240}px`; // 卡片宽度一半左右
cardPosition.top = `${pixelPos.y - 160}px`; // 根据卡片高度调整
cardPosition.visible = true;
console.log(`城市 ${cityName} 的像素坐标: x=${pixelPos.x}, y=${pixelPos.y}`);
}
// 将地理坐标转换为屏幕像素坐标
const convertGeoToPixel = (geoCoord) => {
if (!myChart) return { x: 0, y: 0 };
// 使用 series 而不是 geo 来转换坐标
const pixelCoord = myChart.convertToPixel('series', geoCoord);
// 确保返回有效的坐标
if (Array.isArray(pixelCoord) && pixelCoord.length >= 2) {
return { x: pixelCoord[0], y: pixelCoord[1] };
} else {
console.error('坐标转换失败:', pixelCoord);
return { x: 0, y: 0 };
}
};
</script>
<style scoped lang="scss">
.commonData {
width: 100vw;
height: 100vh;
box-sizing: border-box;
min-width: 100vw;
height: 100vh;
background-color: #000;
display: flex;
flex-direction: column;
}
p {
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
.city-card {
position: fixed;
color: #fff;
transition: all .3s;
p {
margin: 0;
}
.info {
width: 260px;
background: linear-gradient(180deg, #040239 0%, rgba(0, 20, 102, 0.7) 100%);
box-shadow: inset 0px 0px 10px 0px #51B4FC;
border-radius: 12px;
// border: 2px solid;
border-image: linear-gradient(360deg, rgba(83.00000265240669, 182.00000435113907, 254.00000005960464, 1), rgba(30.00000011175871, 113.00000086426735, 210.00000268220901, 0.14000000059604645)) 2 2;
padding: 10px;
box-sizing: border-box;
.sign {
background: rgba(0, 18, 35, 0.7);
border-radius: 8px 8px 8px 8px;
border: 1px solid;
border-radius: 12px;
padding: 10px;
border-image: linear-gradient(180deg, rgba(24.514065384864807, 94.71159741282463, 221.60714864730835, 0), rgba(24.514065384864807, 94.71159741282463, 221.60714864730835, 1)) 1 1;
display: flex;
.col {
flex: 1;
}
}
.sign:last-child {
margin-top: 8px;
}
.top {
p {
font-size: 14px;
color: #FFFFFF60;
}
span {
font-weight: 700;
font-size: 24px;
color: #00F9DE;
}
}
.bom {
span {
font-weight: 400;
font-size: 14px;
color: #FFFFFF60;
}
.num {
font-weight: 700;
font-size: 18px;
color: #FFFFFF;
margin-left: 10px;
}
}
}
}
</style>
完整代码 geo
<template>
<div class="commonData">
<div id="map"></div>
<div class="city-card" :style="{
left: cardPosition.left,
top: cardPosition.top,
visibility: cardPosition.visible ? 'visible' : 'hidden'
}">
<div class='info'>
<div class='sign top'>
<div class='col'>
<p>用户总数</p>
<span>2,283</span>
</div>
<div class='col'>
<p>在线用户</p>
<span>824</span>
</div>
</div>
<div class='sign bom'>
<div class='col'>
<span>今日入职</span>
<span class="num">254</span>
</div>
<div class='col'>
<span>今日离职</span>
<span class="num">12</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import axios from 'axios';
import { onMounted, ref, onUnmounted, reactive } from 'vue';
// 设置需要轮流高亮的城市列表
const citiesToHighlight = [
'北京市',
'上海市',
'广东省',
'江苏省',
'浙江省',
// 可以添加更多城市
];
let currentCityIndex = 0;
const highlightInterval = 2000; // 2秒切换一次
let highlightTimer = null;
let myChart = null;
const cityListData = ref([]);
const cardPosition = reactive({
left: '0px',
top: '0px',
visible: false
});
const getHtmlDom = (str) => {
let htmlElement = document.querySelector(str);
htmlElement.innerHTML = ''
htmlElement.removeAttribute('_echarts_instance_')
htmlElement.removeAttribute('style')
return htmlElement
}
onMounted(() => {
axios.get('https://d.xzxo.cn/i/100000_full.json').then(res => {
cityListData.value = res.data
initChart(res.data);
});
});
const getCityCenter = (cityName) => {
for (let i = 0; i < cityListData.value.features.length; i++) {
let item = cityListData.value.features[i].properties
if (item.name == cityName) {
return item.center
}
}
}
// 组件卸载时清除定时器
onUnmounted(() => {
if (highlightTimer) {
clearInterval(highlightTimer);
}
if (myChart) {
myChart.dispose();
}
});
const initChart = (mapData) => {
let html = getHtmlDom('#map');
echarts.registerMap('China', mapData);
myChart = echarts.init(html);
// 配置项
const option = {
backgroundColor: 'transparent', // 透明背景
geo: {
map: 'China',
roam: false, // 禁用缩放和平移,以保持与设计一致
zoom: 1.5, // 适当放大
center: [104, 35], // 地图中心位置
itemStyle: {
areaColor: '#0D56AA90', // 默认区域颜色-深蓝半透明
borderColor: '#8CD1FF', // 边界线颜色-蓝色
borderWidth: 1.1, // 边界线宽度
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
emphasis: {
itemStyle: {
areaColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1, // 垂直渐变,对应CSS中的180deg
colorStops: [{
offset: 0,
color: '#33AFFF' // 渐变起始颜色
}, {
offset: 1,
color: '#3D75D6' // 渐变结束颜色
}]
},
borderWidth: 1.5,
shadowColor: 'rgba(51, 175, 255, 0.7)',
shadowBlur: 10
},
label: {
show: false // 不显示区域名称
}
},
},
};
myChart.setOption(option);
// 立即执行一次高亮
highlightCity();
// 设置定时轮播高亮
highlightTimer = setInterval(highlightCity, highlightInterval);
// 响应窗口大小变化
window.addEventListener('resize', () => {
myChart.resize();
});
};
// 高亮当前城市并移动到下一个
const highlightCity = () => {
if (!myChart || citiesToHighlight.length === 0) return;
const previousIndex = (currentCityIndex - 1 + citiesToHighlight.length) % citiesToHighlight.length;
const cityToHighlight2 = citiesToHighlight[previousIndex];
// 先取消所有高亮
myChart.dispatchAction({
type: 'geoUnSelect',
name: cityToHighlight2
});
// 当前要高亮的城市
const cityToHighlight = citiesToHighlight[currentCityIndex];
// 高亮当前城市
myChart.dispatchAction({
type: 'geoSelect',
name: cityToHighlight
});
updateCardPosition(cityToHighlight)
console.log(`当前高亮: ${cityToHighlight}`);
// 更新索引,指向下一个城市
currentCityIndex = (currentCityIndex + 1) % citiesToHighlight.length;
};
const updateCardPosition = (cityName) => {
const coords = getCityCenter(cityName);
if (!coords || !myChart) {
cardPosition.visible = false;
return;
}
console.log(coords);
// 将地理坐标转换为屏幕像素坐标
const pixelPos = convertGeoToPixel(coords);
// 设置卡片位置 (偏移一点以便不遮挡城市标记)
cardPosition.left = `${pixelPos.x - 320}px`;
cardPosition.top = `${pixelPos.y - 60}px`;
cardPosition.visible = true;
console.log(`城市 ${cityName} 的像素坐标: x=${pixelPos.x}, y=${pixelPos.y}`);
}
// 将地理坐标转换为屏幕像素坐标
const convertGeoToPixel = (geoCoord) => {
if (!myChart) return { x: 0, y: 0 };
// 使用ECharts内置方法将地理坐标转为像素坐标
const pixelCoord = myChart.convertToPixel('geo', geoCoord);
// 确保返回有效的坐标
if (Array.isArray(pixelCoord)) {
return { x: pixelCoord[0], y: pixelCoord[1] };
} else {
return { x: 0, y: 0 };
}
};
</script>
<style scoped lang="scss">
.commonData {
width: 100vw;
height: 100vh;
box-sizing: border-box;
min-width: 100vw;
height: 100vh;
background-color: #000;
display: flex;
flex-direction: column;
}
p {
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
.city-card {
position: fixed;
color: #fff;
transition: all .3s;
p {
margin: 0;
}
.info {
width: 260px;
background: linear-gradient(180deg, #040239 0%, rgba(0, 20, 102, 0.7) 100%);
box-shadow: inset 0px 0px 10px 0px #51B4FC;
border-radius: 12px;
// border: 2px solid;
border-image: linear-gradient(360deg, rgba(83.00000265240669, 182.00000435113907, 254.00000005960464, 1), rgba(30.00000011175871, 113.00000086426735, 210.00000268220901, 0.14000000059604645)) 2 2;
padding: 10px;
box-sizing: border-box;
.sign {
background: rgba(0, 18, 35, 0.7);
border-radius: 8px 8px 8px 8px;
border: 1px solid;
border-radius: 12px;
padding: 10px;
border-image: linear-gradient(180deg, rgba(24.514065384864807, 94.71159741282463, 221.60714864730835, 0), rgba(24.514065384864807, 94.71159741282463, 221.60714864730835, 1)) 1 1;
display: flex;
.col {
flex: 1;
}
}
.sign:last-child {
margin-top: 8px;
}
.top {
p {
font-size: 14px;
color: #FFFFFF60;
}
span {
font-weight: 700;
font-size: 24px;
color: #00F9DE;
}
}
.bom {
span {
font-weight: 400;
font-size: 14px;
color: #FFFFFF60;
}
.num {
font-weight: 700;
font-size: 18px;
color: #FFFFFF;
margin-left: 10px;
}
}
}
}
</style>