echarts地图贴图(非纹理贴图)的完美实现

2,525 阅读2分钟

背景

设计稿里面需要对地图进行贴背景图处理(显示山川等纹理),公司目前主要使用图表展示技术是Echarts,而Echarts本身的纹理贴图十分难用,因此需要根据Echarts特性做一些改造。

过程

分析

该图里面较难的其实主要是实现背景图

研发

  1. 该项目需要用到一份Svg【地图数据

image.png 从该网站分别下载svg数据。

  1. 处理svg数据(重点)

2.1. 找ui或者打开在线ps,修改path的名称并导出成svg。

1673233255972.jpg

2.2. 打开svg, 将path的id改为name

image.png

2.3. 实现贴图(重点)

让ui提供地图的背景图,并通过引入的方式,加到svg里面

image.png

此时,打开svg,就可以看到对应的效果

image.png

  1. echarts注册对应的数据

image.png

  1. 实现
import { china } from '@tsintergy/geo-json';
import * as echarts from 'echarts';
import type { FC } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
/** @ts-expect-error */
import chinaSvg from './assets/china.svg';
/** @ts-expect-error */
import stationImg from './assets/energyStorageStationIcon.png';
import { convertData, data } from './data';
import './index.less';

type Props = Record<string, unknown>;

const Map2: FC<Props> = () => {
  const getColor = useCallback(
    (value: number, visualMapSelect: boolean[] = [true, true, true, true]) => {
      if (value >= 0 && value < 25 && visualMapSelect[3]) {
        return '#12EEA388';
      }
      if (value >= 25 && value < 50 && visualMapSelect[2]) {
        return '#F49B1688';
      }
      if (value >= 50 && value < 75 && visualMapSelect[1]) {
        return '#EE5C7F88';
      }
      if (value >= 75 && value <= 100 && visualMapSelect[0]) {
        return '#8F7DFF88';
      }
      return '#eee';
    },
    [],
  );

  const option = useMemo(() => {
    return {
      tooltip: {
        trigger: 'item',
        backgroundColor: 'rgba(1,14,13,0.77)',
        borderColor: '#41FFFF',
        borderWidth: 2,
        padding: 8,
        textStyle: {
          color: '#fff',
        },
      },
      visualMap: {
        show: true,
        left: 20,
        bottom: 20,
        type: 'piecewise',
        pieces: [
          { gte: 75, label: '≥75%' },
          { gte: 50, lt: 75, label: '50%-75%' },
          { gte: 25, lt: 50, label: '25%-50%' },
          { gte: 0, lt: 25, label: '0%-25%' },
        ],
        inRange: {
          color: ['#12EEA3', '#F49B16', '#EE5C7F', '#8F7DFF'],
        },
        seriesIndex: [0, 1],
        textStyle: {
          color: '#fff',
        },
      },
      geo: {
        map: 'chinaSvg',
        show: true,
        aspectScale: 0.85,
        zoom: 2,
        roam: true,
        // center: [650, 400],
        label: {
          normal: {
            show: false,
          },
          emphasis: {
            show: false,
          },
        },

        itemStyle: {
          normal: {
            areaColor: '#00000020',
            borderColor: '#97F6FFcc', // 线
            borderWidth: 2,
          },
          emphasis: {
            areaColor: '#97F6FF80', // 悬浮区背景
          },
        },
        regions: data?.map((item) => {
          return {
            name: item.name,
            itemStyle: {
              areaColor: getColor(item.response),
            },
          };
        }),
      },
      series: [
        {
          symbolSize: 25,
          geoIndex: 0,
          label: {
            formatter: '{b}',
            position: 'right',
            show: true,
          },
          itemStyle: {
            normal: {
              color: '#fff',
            },
          },
          name: '虚拟电厂',
          type: 'scatter',
          coordinateSystem: 'geo',
          data: convertData(
            data?.map((item) => {
              return { name: item.name, value: item.response };
            }) || [],
          ),
          // eslint-disable-next-line global-require
          symbol: `image://${stationImg}`,
        },
      ],
    };
  }, [getColor]);

  useEffect(() => {
    window
      .fetch(chinaSvg)
      .then((res) => {
        return res.text();
      })
      .then((e) => {
        const _chinaSvg = e.replace(
          '/assets/ui-example/Map/Map2/1.png',
          // eslint-disable-next-line no-undef
          `${projectName}/assets/ui-example/Map/Map2/1.png`,
        );
        echarts.registerMap('chinaSvg', { svg: _chinaSvg });
        echarts.registerMap('china', china as any);
        const echartDom = document.getElementById('echarts')!;
        const echartsInstance = echarts.init(echartDom);
        echartsInstance.setOption(option as any);

        echartsInstance.on('datarangeselected', function (obj: { selected: any }) {
          const currentOption: any = echartsInstance.getOption();
          currentOption.geo[0].regions = data.map((item) => {
            return {
              name: item.name,
              itemStyle: {
                areaColor: getColor(item.response, obj.selected),
              },
            };
          });

          echartsInstance.setOption(currentOption);
        });
      });
  }, [getColor, option]);

  return (
    <div className={`Map2`} style={{ width: '100%' }}>
      <div id="echarts" style={{ width: '100%', height: '500px' }} />
    </div>
  );
};

export default Map2;
  1. 修改数据,使背景跟地图贴合,及删除多余数据

image.png

image.png

image.png

结果

1.gif

本示例完美实现贴图,及放大缩小平移,如果觉得有用的话,请点赞收藏。