echarts 地图缩放

223 阅读3分钟

功能

屏幕截图 2025-05-29 150313.png

  1. 点击地图,地图放大,地图中心点移到鼠标点击位置
  2. 点击按钮重置,地图初始化原来的状态

地图缩放演示
源代码地址

代码实现

option.ts
echarts初始化配置option,tooltip配置弹框,geo配置中国地图底图,geo.silent为false用来开启地图底图事件监听,geo.zoom指定缩放的大小,geo.emphasis指定激活时的样式。

export const mapOption = {
  tooltip: {
    trigger: 'item',
    position: function (point: any[]) {
      // 默认返回值为相对于视图坐标系的绝对像素位置
      return [point[0] + 10, point[1]]; // 在鼠标位置的基础上向右偏移10px
    },
    formatter: function (params: {
      seriesName?:string;
      data: {
        code?:string;
        name?:string;
        value:string[];
      };
    }) {
      return (
        '经度:' +
        params.data.value[0] +
        '<br/>' +
        '纬度:' +
        params.data.value[1]
      );
    },
  },
  geo: {
    show: true,
    map: 'china',
    roam: true,
    zoom: 1.2,//缩放
    silent: false,//开启事件监听,开启后监听事件能监听到
    center:[103.823557, 36.058039],//地图中心位置

    scaleLimit: {
      min: 1,
      max: 20,
    },
    label: {
      show: false,
      color: '#fff',
    },
    itemStyle: {
      areaColor: '#0197FE',
      borderColor: '#fff',
    },
    emphasis: {//激活样式
      label: {
        color: '#fff',
      },
      itemStyle: {
        areaColor: '#0FCFFF',
      },
    },
  },
  series: [],
};

chart.ts
封装echarts地图管理类class InitChart。包含方法如下:

  1. init(): 初始化地图
    初始化echarts,注册china.json,绑定事件
  2. destoryChart(): 销毁地图
    解除事件,销毁echarts实例对象
  3. setOption(): 重新渲染地图
    通过chart.setOption()方法更新地图渲染展示
  4. restore(): 把地图重置为原来的状态 通过chart.setOption()初始配置把地图初始化
  5. resize(): 屏幕大小适配
  6. bindEvent(): 绑定window的resize事件
  7. destoryEvent(): 解绑window的resize事件
  8. bindClick(): 绑定chart的click事件
import * as echarts from 'echarts';
import chinaMap from './china.json';
/**
 *判断对象是否是一个纯粹的对象
 */
export function isPlainObject(obj: any) {
  return typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 *深度合并多个对象的方法
 */
export function deepAssign(target: any, source: any) {
  if (isPlainObject(source)) {
    if (!isPlainObject(target)) {
      target = {};
    }
    for (let s in source) {
      if (s === '__proto__' || target === source[s]) {
        continue;
      }
      if (isPlainObject(source[s])) {
        target[s] = deepAssign(target[s], source[s]);
      } else {
        target[s] = source[s];
      }
    }
    return target;
  }
}

export class InitChart {
  chart: echarts.ECharts | null = null;
  option: any = null;
  clock: any = null;
  init(chartDom: HTMLElement, option: any, isMap: boolean = false) {
    this.chart = echarts.init(chartDom);
    localStorage.setItem('mapZoom','1.2')//localStorage存储

    isMap && echarts.registerMap('china', chinaMap as any);//注册
    this.setOption(option);
    this.bindEvent();
  }

  destoryChart() {
    this.destoryEvent();
    this.chart && this.chart.dispose();//释放对象
    this.chart = null;
  }

  setOption(option: any) {
    this.chart && this.chart.setOption(option);
    this.option = option;
  }
  restore() {//重置原来的状态
    let option = {
      geo: {zoom: 1.2,center:[103.823557, 36.058039]}//初始缩放和中心位置
    }
    localStorage.setItem('mapZoom','1.2');
    this.chart && this.chart.setOption(option);
  }

  resize() {
    this.clock = setTimeout(() => {
      clearTimeout(this.clock);
      this.chart && this.chart.resize();
    }, 300);
  }

  bindEvent() {
    window.addEventListener('resize', this.resize.bind(this));
  }

  destoryEvent() {
    window.removeEventListener('resize', this.resize.bind(this));
  }

  bindClick(callback?: (event: echarts.ECElementEvent) => void) {
    this.chart?.on('click', (event) => {//click事件绑定
      callback && callback(event);
    });
  }
}

map.vue
给option添加散点series,展示散点。
在onMounted钩子函数初始化chart,绑定点击事件。
在点击事件处理函数中,通过convertFromPixel()函数获取点击位置的经纬度,center的位置在点击的位置的经纬度,zoom在原来的基础上加1。然后通过setOption设置center和zoom的值。
重置reset调用chart的.restore()方法初始化地图展示。

<template>
  <div class="item">
    <div class="header">
      <n-button strong secondary type="primary" size="small" @click="reset">重置</n-button>
    </div>
    <div class="content">
      <div id="mapChart" ref="mapChart" class="w-full h-full"></div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { Ref, onBeforeUnmount, onMounted, ref } from 'vue';
import { InitChart } from './chart';
import { mapOption } from './option';

let option: any = mapOption;
const mapChart: Ref<HTMLElement | null> = ref(null);
let chart: any = null;

interface SeriesDataItem {
  name: string;
  num?: number;
  value: [number, number, number?, any?];
}
interface MapSeriesItem {
  name: string;
  type: string;
  coordinateSystem: string;
  symbolSize?: number;
  itemStyle: { color: string };
  data: SeriesDataItem[];
}
const series: MapSeriesItem[] = [
  {
    name: '正常',
    type: 'scatter',
    coordinateSystem: 'geo',
    symbolSize: 8,
    itemStyle: {
      color: '#00FF6C',
    },
    data: [
      { name: '散点1', value: [116.52, 40, 1] },
      { name: '散点2', value: [116.52, 40, 1] },
      { name: '散点3', value: [116.52, 40, 1] },
      
    ],
  },
  {
    name: '停运',
    type: 'scatter',
    coordinateSystem: 'geo',
    symbolSize: 8,
    itemStyle: {
      color: '#FF3000',
    },
    data: [
      { name: '散点4', value: [117.52, 36, 1] },
      { name: '散点5', value: [113.52, 22, 1] },
      { name: '散点6', value: [113.52, 24, 42] },
    ],
  },
];
option.series = series;
function reset() {
  chart.restore();
}

onMounted(() => {//挂载后
  chart = new InitChart();
  chart.init(mapChart.value as HTMLElement, option, true);
  //点击
  chart.bindClick((params: any) => {
     //点击位置的经纬度
     let center = chart.chart.convertFromPixel('geo', [params.event.offsetX, params.event.offsetY]);
     if (center) {
      
      let zoom = localStorage.getItem('mapZoom') || '1.2';
      let zoomSize = Number(zoom) + 1;
      localStorage.setItem('mapZoom',zoomSize+'')
      chart.setOption({
        geo: { zoom: zoomSize, center: center},
      });
     }
  });
});
onBeforeUnmount(() => {
  chart.destoryChart();
});
</script>
<style lang="scss" scoped>
.item {
  background-color: rgba(255,255,255,0.9);
  border-radius: 4px;
  -webkit-border-radius: 4px;
  margin: 10px;
  padding-top:10px;
  box-shadow:  0px 0px 12px rgba(0, 0, 0, .12);
  .header {
    display: flex;
    justify-content: center;
  }
  .content {
    height: 500px;
  }
}
</style>