mapboxgl动态点图标制作

mapboxgl动态点图标制作

interface PulsingDot {
  width: number;
  height: number;
  data: Uint8Array;
  context: CanvasRenderingContext2D | null;
  onAdd: () => void;
  render: () => boolean;
}

class AnimatePointLayer {
  private map: mapboxgl.Map | undefined;
  private layerId: string;
  private iconName: string;
  private lngLat: GeoJSON.Position;

  private animatePointGeoJson: GeoJSON.FeatureCollection<GeoJSON.Point>;
  constructor(lngLat?: GeoJSON.Position) {
    this.layerId = 'ship-dot-point';
    this.iconName = 'pulsing-dot';
    this.lngLat = lngLat ?? [];
    // 动态点的 GeoJSON 数据
    this.animatePointGeoJson = {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          properties: {},
          geometry: {
            type: 'Point',
            coordinates: [],
          },
        },
      ],
    };
  }

  /**
   * 初始化方法
   */
  private initialize() {
    this.addIcon();
  }
  /**
   * 更新点图标位置
   * @param lngLat
   */
  public setLngLat(lngLat: GeoJSON.Position) {
    let feature = this.animatePointGeoJson.features.at(0);
    if (feature) feature.geometry.coordinates = lngLat;
    let source = this.map?.getSource(this.layerId) as mapboxgl.GeoJSONSource;
    if (source) {
      source.setData(this.animatePointGeoJson);
      let poi = lngLat as any;
      this.map?.flyTo({ center: poi, zoom: 5 });
    } else {
      console.error('请先将实例添加至地图');
    }
  }
  /**
   * 添加图层
   */
  public addTo(map: mapboxgl.Map) {
    this.map = map;
    this.initialize();
    let source = this.map?.getSource(this.layerId);
    if (source) return;
    this.map?.addLayer({
      id: this.layerId,
      type: 'symbol',
      source: {
        type: 'geojson',
        data: this.animatePointGeoJson,
      },
      layout: {
        'symbol-sort-key': 999,
        'icon-image': this.iconName,
        'icon-allow-overlap': true,
      },
    });
    if (this.lngLat.length) this.setLngLat(this.lngLat);
    return this;
  }
  /**
   * 移除图层
   */
  public remove() {
    if (this.map?.getLayer(this.layerId)) {
      this.map.removeLayer(this.layerId);
    }
    if (this.map?.getSource(this.layerId)) {
      this.map.removeSource(this.layerId);
    }
  }
  /**
   * 添加动态点图标
   */
  private addIcon() {
    let map = this.map;
    if (map?.hasImage(this.iconName)) return;
    let size = 150;
    let pulsingDot: PulsingDot = {
      width: size,
      height: size,
      data: new Uint8Array(size * size * 4),
      context: null,
      onAdd: function () {
        const canvas = document.createElement('canvas');
        canvas.width = this.width;
        canvas.height = this.height;
        this.context = canvas.getContext('2d');
      },

      render: function () {
        const duration = 1000; // 动画的持续时间为 1000 毫秒(1 秒)
        const t = (performance.now() % duration) / duration; // 计算当前时间相对于动画周期的比例

        const radius = (size / 2) * 0.1; // 内圆的半径
        const outerRadius = (size / 2) * 0.9 * t + radius; // 动态计算外圆的半径
        const context = this.context!;

        // 清除画布
        context?.clearRect(0, 0, this.width, this.height);

        // 画外圆
        context.beginPath();
        context.arc(this.width / 2, this.height / 2, outerRadius, 0, Math.PI * 2);
        context.fillStyle = `rgba(255, 200, 200, ${1 - t})`; // 外圆的填充颜色,随着时间变化透明度减少
        context.fill();

        // 画内圆
        context.beginPath();
        context.arc(this.width / 2, this.height / 2, radius, 0, Math.PI * 2);
        context.fillStyle = 'rgba(255, 100, 100, 1)'; // 内圆的填充颜色
        context.strokeStyle = 'rgba(255, 100, 100, 1)'; // 内圆的描边颜色
        context.lineWidth = 2 + 4 * (1 - t); // 内圆的描边宽度,随着时间变化宽度减少
        context.fill();
        context.stroke();

        // 获取画布的数据
        this.data = context.getImageData(0, 0, this.width, this.height).data as any;

        // 触发地图重新绘制以生成平滑的动画效果
        if (typeof map !== 'undefined' && map.triggerRepaint) {
          map.triggerRepaint();
        }

        // 返回 true 以通知地图图像已更新
        return true;
      },
    };
    map?.addImage(this.iconName, pulsingDot, { pixelRatio: 2 });
  }
}
export default AnimatePointLayer;
posted @ 2024-07-16 23:26  槑孒  阅读(4)  评论(0编辑  收藏  举报