vue2 实现openlayers添加扩散点(水波纹效果)

vue2 实现openlayers添加扩散点(水波纹效果)

  • 工具类 PulsingPointManager.js
import Overlay from 'ol/Overlay';

/**
 * 扩散点管理器类
 */
export default class PulsingPointManager {
  /**
   * @param {ol.Map} map - OpenLayers 地图实例
   */
  constructor(map) {
    if (!map) {
      throw new Error('Map instance is required');
    }
    this.map = map;
    // 使用 Map 存储 overlay,key 为自定义 ID,value 为 Overlay 实例
    // 如果没有 ID,则使用随机生成的 UUID
    this.overlays = new Map();
  }

  /**
   * 生成简单的唯一 ID
   */
  _generateId() {
    return 'pulsing-' + Math.random().toString(36).substr(2, 9);
  }

  /**
   * 创建扩散点的 DOM 元素
   * @param {string} color - 颜色 (可选)
   * @returns {HTMLElement}
   */
  _createDOMElement(color = 'rgba(255, 0, 0, 0.8)') {
    const element = document.createElement('div');
    element.className = 'pulsing-point';
    const style = document.createElement('style');
    style.textContent = `
      .pulsing-point[data-color="${color}"]::before { background-color: ${color}; }
      .pulsing-point[data-color="${color}"]::after { background-color: ${color.replace('0.8', '0.6')}; }
    `;
    element.setAttribute('data-color', color);
    return element;
  }

  /**
   * 添加一个扩散点
   * @param {Array<number>} coordinate - 经纬度坐标 [lng, lat]
   * @param {string} id - 唯一标识 (可选,如果不传则自动生成)
   * @param {string} color - 颜色 (可选)
   * @returns {string} - 返回该点的 ID
   */
  add(coordinate, id = null, color = 'rgba(255, 0, 0, 0.8)') {
    const uniqueId = id || this._generateId();

    // 如果该 ID 已存在,先移除旧的
    if (this.overlays.has(uniqueId)) {
      this.remove(uniqueId);
    }

    const element = this._createDOMElement(color);
    
    const overlay = new Overlay({
      element: element,
      position: coordinate,
      positioning: 'center-center',
      stopEvent: false,
      insertFirst: false
    });

    this.map.addOverlay(overlay);
    this.overlays.set(uniqueId, overlay);

    return uniqueId;
  }

  /**
   * 移除指定的扩散点
   * @param {string} id - 唯一标识
   */
  remove(id) {
    if (!id || !this.overlays.has(id)) return;

    const overlay = this.overlays.get(id);
    this.map.removeOverlay(overlay);
    
    // 清理 DOM 引用
    overlay.setElement(null);
    this.overlays.delete(id);
  }

  /**
   * 获取指定的 Overlay 实例
   * @param {string} id 
   * @returns {Overlay|undefined}
   */
  get(id) {
    return this.overlays.get(id);
  }

  /**
   * 清空所有扩散点
   */
  clearAll() {
    this.overlays.forEach((overlay) => {
      this.map.removeOverlay(overlay);
      overlay.setElement(null);
    });
    this.overlays.clear();
  }

  /**
   * 销毁管理器(清空所有并解除地图引用)
   */
  destroy() {
    this.clearAll();
    this.map = null;
  }
}
  • 将下面的css样式放到全局的css样式文件中或放在app.vue
.pulsing-point {
  position: relative;
  width: 20px;
  height: 20px;
  transform: translate(-50%, -50%);
  /* 默认颜色 */
  --point-color: rgba(255, 0, 0, 0.8);
}

.pulsing-point::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: var(--point-color);
  border-radius: 50%;
  z-index: 2;
  border: 1px solid #fff;
}

.pulsing-point::after {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-color: var(--point-color);
  opacity: 0.6; /* 使用固定透明度,配合动画 */
  border-radius: 50%;
  z-index: 1;
  animation: ripple 2s infinite ease-out;
}

@keyframes ripple {
  0% { transform: scale(1); opacity: 0.6; }
  100% { transform: scale(4); opacity: 0; }
}
  • 组件中使用
<template>
  <div>
    <div id="map-container" style="width: 100%; height: 500px;"></div>
    <div class="controls">
      <button @click="addBeijing">添加北京点</button>
      <button @click="removeBeijing">移除北京点</button>
      <button @click="clearAll" style="color: red;">清空所有</button>
    </div>
  </div>
</template>

<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';

// 引入工具类
import PulsingPointManager from './utils/PulsingPointManager';

export default {
  data() {
    return {
      map: null,
      pulsingManager: null, // 管理器实例
    };
  },
  mounted() {
    this.initMap();
  },
  methods: {
    initMap() {
      this.map = new Map({
        target: 'map-container',
        layers: [
          new TileLayer({
            source: new XYZ({
              url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            }),
          }),
        ],
        view: new View({
          center: [0, 0],
          zoom: 2,
        }),
      });

      // 初始化管理器
      this.pulsingManager = new PulsingPointManager(this.map);
    },
    addBeijing() {
      // 添加点位,指定 ID 为 'bj',颜色为红色
      this.pulsingManager.add([116.397428, 39.90923], 'bj', 'rgba(255, 0, 0, 0.8)');
    },
    removeBeijing() {
      this.pulsingManager.remove('bj');
    },
    clearAll() {
      this.pulsingManager.clearAll();
    }
  },
  beforeDestroy() {
    // 组件销毁前,务必销毁管理器以释放内存
    if (this.pulsingManager) {
      this.pulsingManager.destroy();
    }
    if (this.map) {
      this.map.setTarget(null);
    }
  },
};
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码工人笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值