需求背景:在一张地图底图上把市区县划分出来,鼠标移入某个区县高亮显示,本来想用div+绝对定位、css的hover实现,但是每个区域都是不规则的,会导致区域重叠,所以放弃。
研究过后有几种方案可以实现 1. 使用地图api 2. echarts地图 3. svg
具体样式如下,鼠标划过某个区域高亮 因为项目时间紧急,所以我用的svg方式,下面是demo,把鼠标移入高亮 封装为一个子组件,然后在父组件传入每一块高亮的svg
1.svg
子组件:
ini
<template>
<div id="svg" ref="hello"></div>
</template>
<script setup>
import { createApp, onMounted, ref } from 'vue/dist/vue.esm-bundler.js';
// import svgIcon from '../assets/svgicon/hover_img_shanghe.svg'; // 商河县
import { defineProps, defineEmits } from 'vue';
let svgEl = null;
const hello = ref(null);
const props = defineProps({
svgIcon: String,
name: String
});
let emit = defineEmits(['click-name', 'fillOpacity']);
// 点击事件
const clickName = () => emit('click-name', props.name);
function handleMouseenter(e) {
// console.log(e, 12345, svgEl);
const pathList = [...svgEl._container.getElementsByTagName('path')];
pathList.forEach(item => {
if (item.getAttribute('fill-opacity')) {
item.setAttribute('fill-opacity', 0.5);
emit('fillOpacity', 0.5);
} else {
item.setAttribute('opacity', 1);
}
});
}
function handleMouseleave() {
const pathList = [...svgEl._container.getElementsByTagName('path')];
pathList.forEach(item => {
if (item.getAttribute('fill-opacity')) {
item.setAttribute('fill-opacity', 0);
emit('fillOpacity', 0);
} else {
item.setAttribute('opacity', 0);
}
});
}
onMounted(() => {
const xhr = new XMLHttpRequest();
xhr.open('GET', props.svgIcon, true);
xhr.send();
xhr.addEventListener('load', () => {
// console.log('load', xhr);
const resXML = xhr.responseXML;
const svgDom = resXML.documentElement.cloneNode(true);
// console.log(svgDom.getElementsByTagName('path'));
const paths = svgDom.getElementsByTagName('path');
const pathList = [...paths];
pathList.forEach(item => {
if (item.getAttribute('fill-opacity')) {
item.setAttribute('fill-opacity', 0);
} else {
item.setAttribute('opacity', 0);
}
});
paths[0].setAttribute('v-on:mouseenter', 'handleMouseenter()');
paths[0].setAttribute('v-on:mouseleave', 'handleMouseleave()');
paths[0].setAttribute('v-on:click', 'clickName()');
const oSerializer = new XMLSerializer();
const sXML = oSerializer.serializeToString(svgDom);
const Profile = createApp({
template: "<div id='svgTemplate'>" + sXML + '</div>',
methods: {
handleMouseenter: () => {
handleMouseenter();
},
handleMouseleave: () => {
handleMouseleave();
},
clickName: () => {
clickName();
}
}
});
svgEl = Profile;
// console.log('=====', hello.value);
// 创建实例,并挂载到元素上
svgEl.mount(hello.value);
});
});
</script>
父组件调用:
ini
<MapSvg
class="shanghe-img"
:svg-icon="svgUrl"
name="shanghe"
@click-name="clickAreaName" // 点击图片左边列表对应区县高亮
@fillOpacity="getOpacity"> // 获取透明度
</MapSvg>
const svgUrl = ref(require('../assets/svgicon/hover_img_shanghe.svg')); // svg图片
- echarts地图(这个暂时没有用到)
xml
<!DOCTYPE html>
<html lang="zh-CN" style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div>
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<!-- Uncomment this line if you want to dataTool extension
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.3/dist/extension/dataTool.min.js"></script>
-->
<!-- Uncomment this line if you want to use gl extension
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts-gl@2/dist/echarts-gl.min.js"></script>
-->
<!-- Uncomment this line if you want to echarts-stat extension
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts-stat@latest/dist/ecStat.min.js"></script>
-->
<!-- Uncomment this line if you want to use map
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@4.9.0/map/js/china.js"></script>
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@4.9.0/map/js/world.js"></script>
-->
<!-- Uncomment these two lines if you want to use bmap extension
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=YOUR_API_KEY"></script>
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.3/dist/extension/bmap.min.js"></script>
-->
<script type="text/javascript">
var dom = document.getElementById('container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
$.get('https://digital-education-platform.oss-cn-beijing.aliyuncs.com/signage/shadow/zhangxu_ditu.svg', function (svg) {
echarts.registerMap('Beef_cuts_France', { svg: svg });
option = {
tooltip: {},
visualMap: {
left: 'center',
bottom: '10%',
min: 5,
max: 100,
orient: 'horizontal',
text: ['', 'Price'],
realtime: true,
calculable: true,
inRange: {
color: ['#B2D57A', '#ff0000', '#ff0000']
}
},
series: [
{
name: 'French Beef Cuts',
type: 'map',
map: 'Beef_cuts_France',
roam: true,
emphasis: {
label: {
show: false
}
},
selectedMode: false,
data: [
{ name: 'diyi', value: 0 },
{ name: 'dier', value: 0 },
{ name: 'disan', value: 0 },
{ name: 'disi', value: 0 },
{ name: 'diwu', value: 0 },
{ name: 'diliu', value: 0 },
{ name: 'diqi', value: 0 },
{ name: 'diba', value: 0 },
{ name: 'dijiu', value: 0 },
{ name: "dishi", value: 0 },,
{ name: "shiyi", value: 0 },,
{ name: "shier", value: 0 },
]
}
]
};
myChart.setOption(option);
});
if (option && typeof option === 'object') {
myChart.setOption(option);
}
window.addEventListener('resize', myChart.resize);
</script>
</body>
</html>
具体样式: