原生 js 实现类 3d 地图大屏展示自动高亮轮播显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生 js 实现类 3d 地图大屏展示自动高亮轮播显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎相关的知识,希望对你有一定的参考价值。

要实现的效果

如下图,3d 地图高亮自动轮播,展示白云区各个镇街的人口数局。

原由

为什么想到这个方案,是因为我在用 echarts-gl 实现 3d 地图效果的过程中,我发现通过 dispatchAction 触发不了 3d 的高亮。不知道大家有没有遇到,所以我实现不出自动轮播的 3d 效果,因为显示悬浮提示 tooltip 不出来,必须要 hover 的时候才行。(不知道是不是我的配置问题,如果大家有好的 3d 地图自动轮播方案,欢迎在下面评论分享。)

于是,我在想能不能用 svg 直接渲染成 dom,然后在定时的修改对应 dom 的样式,并且显示出对应标签的 tooltip 提示。我觉得可以一试,于是我找了一些相关资料,简单的实现了一下。

代码实现

1、修改地图的视觉提供地图的 svg 图

由于 svg 的字符过多,博客放不下,这里就不展示 svg 的全部代码了。

主要的就是为了方便激活地块,快速的找到 dom 元素,我们需要给地块加一些 id,还有标签定位 tooltip,方便获取跟标记。

<g id="plot" name="地块" transform="translate(0.000000, -0.000000)">
  <g id="plot_yuncheng">
    <use fill="#eee" fill-rule="evenodd" xlink:href="#path-47"></use>
    <use fill="black" fill-opacity="1" filter="url(#filter-48)" xlink:href="#path-47"></use>
    <use stroke="#aaa" stroke-width="2" xlink:href="#path-47"></use>
  </g>
...
</g>
<g id="label" name="标签" transform="translate(80.000000, 128.000000)">
	<g id="label_renhe" transform="translate(176.000000, 81.000000)">
	  <g id="编组-77备份-3">
	    <path d="M71.0581411,-0.5 L71.0581411,24.3372098 L-0.5,24.3372098 L-0.5,-0.5 L71.0581411,-0.5 Z"
	      id="矩形备份-37" stroke="#fff" fill="orange"></path>
	    <path
	      d="M70.5581411,19.8643415 L70.5581411,23.8372098 L66.8722681,23.8372098 L70.5581411,19.8643415 Z M3.68587304,0 L0,3.97286831 L0,0 L3.68587304,0 Z"
	      id="形状结合" fill="#fff"></path>
	  </g>
	  <text id="人和镇" font-family="PingFangSC-Medium, PingFang SC" font-size="14" font-weight="400" fill="#FFFFFF">
	    <tspan x="14.1116282" y="16.9186049">人和镇</tspan>
	  </text>
	</g>
	...
</g>

2、引入 popper.js 定位引擎

官网:TOOLTIP & POPOVER POSITIONING ENGINE

第一种使用方式:

npm i @popperjs/core -s
import  createPopper  from '@popperjs/core';
const popcorn = document.querySelector('#popcorn');
const tooltip = document.querySelector('#tooltip');
createPopper(popcorn, tooltip, 
  placement: 'top',
);

第二种使用方式:

html 中引入 popper.js 定位引擎:

<script src="https://unpkg.com/@popperjs/core@2"></script>
<!DOCTYPE html>
<html>
  <head>
    <title>Popper Tutorial</title>
  </head>
  <body>
    <button id="button" aria-describedby="tooltip">My button</button>
    <div id="tooltip" role="tooltip">My tooltip</div>

    <script src="https://unpkg.com/@popperjs/core@2"></script>
    <script>
      const button = document.querySelector('#button');
      const tooltip = document.querySelector('#tooltip');

      const popperInstance = Popper.createPopper(button, tooltip);
    </script>
  </body>
</html>

3、激活地块

需要给 use 元素设置激活的 style 属性

let activedStyle = "fill:blue"; // 地块激活样式
// 激活地块
function addPlotActive(index) 
  baiyunSvgDom.getElementById(`plot_$plotArrs[index]`).getElementsByTagName('use')[0].setAttribute("style", activedStyle);

4、tooltip 定位

我们需要找到当前轮播的标签元素,以及提示框的元素,然后通过 Popper.createPopper 设置就行。

// 设置标签提示定位
function setCreatePopper() 
  Popper.createPopper(baiyunSvgDom.getElementById(`label_$plotArrs[index]`), mapTooltipsDom, 
    placement: 'top-start',
    positionFixed: true
  );

5、自动轮播跟赋值

自动轮播的话主要需要注意第一次跟最后一次就行,另外赋值的时候,innerHTML 不要写错了。具体代码的话看逻辑代码即可。

逻辑代码

这里的代码缺失了 svg 里的内容。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SVG地图轮播</title>
  <style>
    .svg-geo-map-tooltips 
      min-width: 160px;
      background-color: rgba(0,0,0,.8);
      border-radius: 8px;
      text-align: center;
      padding: 18px 8px;
      transition: .4s;
      left: 20px !important;
      bottom: 5px !important;
    
    .svg-geo-map-tooltips .name 
      font-size: 16px;
      font-weight: 400;
      color: #ffffff;
      height: 22px;
      line-height: 22px;
      margin-bottom: 8px;
    
    .svg-geo-map-tooltips .value 
      color: #ffffff;
    
    .svg-geo-map-tooltips .value .num 
      font-size: 24px;
      font-weight: bold;
      height: 32px;
      line-height: 32px;
    
    .svg-geo-map-tooltips .value .unit 
      font-size: 14px;
    
  </style>
  <script src="https://unpkg.com/@popperjs/core@2"></script>
</head>
<body>
  <div class='svg-geo-map-chart'>
    <svg id="baiyunSvg" width="804px" height="597px" viewBox="0 0 804 597" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
    <!-- 提示 -->
    <div class="svg-geo-map-tooltips">
      <div class="name"></div>
      <div class="value">
        <span class="num"></span>
        <span class="unit"></span>
      </div>
    </div>
  </div>

  <script>
    let curPlotName = ""; // 当前激活的地块名
    let curPlotValue = 0; // 当前激活的地块服务量数
    let interval = 1000; // 时间间隔毫秒数
    let index = 0; // 播放所在下标
    let timer = null;
    let activedStyle = "fill:blue"; // 地块激活样式
    let baiyunSvgDom = null; // svg 的 dom 元素
    let mapTooltipsDom = null; // 提示的 dom 元素
    let baiyunPlotDataList = [
      id: "jianggao",
      name: "江高镇",
      value: 883945
    ,
      id: "renhe",
      name: "人和镇",
      value: 4567992
    ,
      id: "taihe",
      name: "太和镇",
      value: 4567323
    ,
      id: "zhongluotan",
      name: "钟落潭镇",
      value: 497863
    ,
      id: "longgui",
      name: "龙归街",
      value: 3486257
    ,
      id: "dayuan",
      name: "大源街",
      value: 897435
    ,
      id: "sanyuanli",
      name: "三元里街",
      value: 46809544
    ,
      id: "songzhou",
      name: "松洲街",
      value: 123403
    ,
      id: "jingtai",
      name: "景泰街",
      value: 342256677
    ,
      id: "tongde",
      name: "同德街",
      value: 234677
    ,
      id: "huangshi",
      name: "黄石街",
      value: 976542
    ,
      id: "tangjing",
      name: "棠景街",
      value: 33456
    ,
      id: "xinshi",
      name: "新市街",
      value: 3455602
    ,
      id: "tonghe",
      name: "同和街",
      value: 487654
    ,
      id: "jingxi",
      name: "京溪街",
      value: 3876735
    ,
      id: "yongping",
      name: "永平街",
      value: 6677544
    ,
      id: "jiahe",
      name: "嘉禾街",
      value: 34526784
    ,
      id: "junhe",
      name: "均禾街",
      value: 8756534
    ,
      id: "shijing",
      name: "石井街",
      value: 220232
    ,
      id: "jinsha",
      name: "金沙街",
      value: 3352256
    ,
      id: "yuncheng",
      name: "云城街",
      value: 335677
    ,
      id: "helong",
      name: "鹤龙街",
      value: 334225
    ,
      id: "baiyunhu",
      name: "白云湖街",
      value: 34556
    ,
      id: "shimen",
      name: "石门街",
      value: 1354667
    ]; // 默认地块数据
    let plotArrs = baiyunPlotDataList.map(el => el.id); // 地块的id数据

    // 数字加千位分隔符
    function numToThsSprtr (num) 
      let res = num.toString().replace(/\\d+/, function (n)  // 先提取整数部分
        return n.replace(/(\\d)(?=(\\d3)+$)/g, function ($1) 
          return $1 + ',';
        );
      )
      return res;
    
    // 初始化
    function initRender() 
      // 获取 dom 元素
      baiyunSvgDom = document.getElementById('baiyunSvg');
      mapTooltipsDom = document.querySelector('.svg-geo-map-tooltips');
      // 地块的id数据
      plotArrs = baiyunPlotDataList.map(el => el.id);
      // 初始化激活颜色
      this.addPlotActive(index);
      // 赋值
      setPlotValue();
      console.log(index, plotArrs[index], curPlotName, curPlotValue);
      // 初始化定位
      this.setCreatePopper();
    
    // 设置轮播
    function setIntervalSvg() 
      timer = setInterval(() => 
        // 先清除上一次的激活效果
        if(index !== -1) 
          removePlotActive(index);
        
        // 索引自增
        index++;
        // 地块激活
        this.addPlotActive(index);
        // 赋值
        setPlotValue();
        console.log(index, plotArrs[index], curPlotName, curPlotValue);
        // 进行提示定位
        this.setCreatePopper();
        // 处理最后一个地块激活问题
        if(index === plotArrs.length - 1) 
          let tempTimer = setTimeout(() => 
            // 移除地块最后一个的激活
            removePlotActive(plotArrs.length - 1);
            clearTimeout(tempTimer);
          , interval);
          index = -1;
        
      , interval);
    
    // 激活地块
    function addPlotActive(index) 
      baiyunSvgDom.getElementById(`plot_$plotArrs[index]`).getElementsByTagName(

以上是关于原生 js 实现类 3d 地图大屏展示自动高亮轮播显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎的主要内容,如果未能解决你的问题,请参考以下文章

threejs可视化大屏酷炫3D地图附源码

原生js实现轮播图

原生JS实现过渡效果的轮播图

Web3D工厂可视化,工厂三维可视化大屏,工厂模型展示,统计图表,人员定位,人车轨迹运动,电子围栏

Echarts的轮播实现方案

百度js地图实现列表与地图上的标注点的联动效果。即点击列表中的一列,实现地图上的标注高亮显示且弹出窗