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>

具体样式:

相关推荐
方安乐3 分钟前
vite+vue+js项目使用ts报错
前端·javascript·vue.js
韩立23335 分钟前
Vue 3.5 升级指南
前端·vue.js
子兮曰13 分钟前
🚀别再乱写package.json了!这些隐藏技巧让项目管理效率提升300%
前端·javascript·npm
我叫汪枫18 分钟前
Spring Boot图片验证码功能实现详解 - 从零开始到完美运行
java·前端·javascript·css·算法·html
小桥风满袖22 分钟前
极简三分钟ES6 - ES8中async,await
前端·javascript
一直在学习的小白~1 小时前
node_modules 明明写进 .gitignore,却还是被 push/commit 的情况
前端·javascript·vue.js
前端小超超1 小时前
如何配置capacitor 打包的ios app固定竖屏展示?
前端·ios·web app
nightunderblackcat1 小时前
新手向:从零理解LTP中文文本处理
前端·javascript·easyui
kyle~2 小时前
python---PyInstaller(将Python脚本打包为可执行文件)
开发语言·前端·python·qt
User:你的影子2 小时前
WPF ItemsControl 绑定
开发语言·前端·javascript