SVG实现鼠标滑过某个区域高亮显示

需求背景:在一张地图底图上把市区县划分出来,鼠标移入某个区县高亮显示,本来想用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图片
  1. 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>

具体样式:

相关推荐
DT——5 小时前
Vite项目中eslint的简单配置
前端·javascript·代码规范
学习ing小白7 小时前
JavaWeb - 5 - 前端工程化
前端·elementui·vue
一只小阿乐7 小时前
前端web端项目运行的时候没有ip访问地址
vue.js·vue·vue3·web端
计算机学姐7 小时前
基于python+django+vue的旅游网站系统
开发语言·vue.js·python·mysql·django·旅游·web3.py
真的很上进7 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er7 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063718 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl8 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码8 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347548 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js