Skip to content

水纹动画

TIP

两种不同的方式实现水纹动画。

代码如下:

点我查看代码
vue
<template>
  <div id="map"></div>
</template>

<script lang="ts" setup>
import { onMounted, onBeforeUnmount } from "vue";
import Feature from "ol/Feature";
import { Map, Overlay } from "ol";
import Point from "ol/geom/Point";
import View from "ol/View";
import { Circle as CircleStyle, Stroke, Style } from "ol/style";
import { XYZ, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { easeOut } from "ol/easing";
import { getVectorContext } from "ol/render";
import { ATTRIBUTIONS, MAPURL, SHENZHEN, FUZHOU } from "../../../constants";
import { addVectorLabel } from "./tools";
import RenderEvent from "ol/render/Event";

let map: Map | null = null;
const initMap = () => {
  const tileLayer = new TileLayer({
    source: new XYZ({
      attributions: ATTRIBUTIONS,
      url: MAPURL,
      maxZoom: 20,
    }),
  });

  const source = new VectorSource({
    wrapX: false,
  });
  const vector = new VectorLayer({
    source: source,
  });

  map = new Map({
    layers: [tileLayer, vector],
    target: "map",
    view: new View({
      projection: "EPSG:4326",
      center: SHENZHEN,
      //地图初始显示级别
      zoom: 5,
      multiWorld: true,
    }),
  });
  const duration = 3000;

  function flash(feature: Feature) {
    let start = Date.now();
    const flashGeom = feature?.getGeometry()?.clone();
    tileLayer.on("postrender", animate);
    function animate(event: RenderEvent) {
      const frameState = event.frameState;
      if (!frameState) return;
      const elapsed = frameState.time - start;
      const vectorContext = getVectorContext(event);
      const elapsedRatio = elapsed / duration;
      // 开始半径为5,结束半径为30。
      const radius = easeOut(elapsedRatio) * 25 + 5;
      const opacity = easeOut(1 - elapsedRatio);

      const style = new Style({
        image: new CircleStyle({
          radius: radius,
          stroke: new Stroke({
            color: "rgba(255, 0, 0, " + opacity + ")",
            width: 0.25 + opacity,
          }),
        }),
      });
      vectorContext.setStyle(style);
      if (flashGeom) {
        vectorContext.drawGeometry(flashGeom);
      }
      if (elapsed > duration) {
        start = frameState.time;
      }
      // 告诉OpenLayers继续后期动画
      map?.render();
    }
  }

  //第一种方式添加水纹动画
  const geom = new Point(SHENZHEN);
  const feature = new Feature(geom);
  source.addFeature(feature);
  flash(feature);
  //添加打工点
  addVectorLabel({
    coordinate: SHENZHEN,
    vectorSource: source,
    name: "打工点",
  });

  //第二种方式添加水纹动画
  const element = document.createElement("div");
  element.className = "point_animation";
  const p = document.createElement("p");
  const span = document.createElement("span");
  element.appendChild(p);
  element.appendChild(span);
  const point_overlay = new Overlay({
    element: element,
    positioning: "center-center",
  });
  map.addOverlay(point_overlay);
  point_overlay.setPosition(FUZHOU);
  //添加家
  addVectorLabel({
    coordinate: FUZHOU,
    vectorSource: source,
    name: "家",
  });
};

onMounted(() => {
  initMap();
});

onBeforeUnmount(() => {
  if (map) {
    map.dispose();
    map = null;
  }
});
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
  position: relative;
  height: 650px;
}
/**橙色点扩散闪烁样式*/
:deep(.point_animation) {
  background: #ff9900;
  width: 6px;
  height: 6px;
  border: 2px #ff9900 solid;
  border-radius: 50%;
  position: absolute;
}
:deep(.point_animation p),
:deep(.point_animation span) {
  position: absolute;
  width: 4px;
  height: 4px;
  animation: point_animation 4s infinite;
  box-shadow: 0px 0px 1px #ff9900;
  margin: 0px;
  border-radius: 50%;
}
:deep(.point_animation span) {
  animation-delay: 1.5s;
}
@keyframes point_animation {
  10% {
    transform: scale(1);
  }
  100% {
    transform: scale(8);
  }
}
</style>

tools 代码如下:

点我查看代码
ts
import { Style, Text, Fill, Stroke } from 'ol/style'
import { Feature } from 'ol'
import { Point } from 'ol/geom'
import { VectorLabelOptions } from '../../../@types'
/**
 * 创建矢量标注样式函数,设置image为图标ol.style.Icon
 * @param {ol.Feature} feature 要素
 */
export const createLabelStyle = (feature: Feature): Style =>
  new Style({
    text: new Text({
      //位置
      textAlign: 'center',
      //基准线
      textBaseline: 'middle',
      //文字样式
      font: 'normal 14px 微软雅黑',
      //文本内容
      text: feature.get('name'),
      //文本填充样式(即文字颜色)
      fill: new Fill({ color: '#aa3300' }),
      stroke: new Stroke({ color: '#ffcc33', width: 2 }),
      offsetY: 40,
    }),
  })

/**
 * 添加一个新的标注(矢量要素)
 * @param {ol.Coordinate} coordinate 坐标点
 * @param {ol.source.Vector} vectorSource 矢量标注的数据源
 * @param {String} name 标注名
 */
export const addVectorLabel = ({
  coordinate,
  vectorSource,
  name = '标注点',
}: VectorLabelOptions) => {
  if (vectorSource === null) return
  //新建一个要素 ol.Feature
  const newFeature = new Feature({
    //几何信息
    geometry: new Point(coordinate),
    //名称属性
    name,
  })
  //设置要素的样式
  newFeature.setStyle(createLabelStyle(newFeature))
  //将新要素添加到数据源中
  vectorSource.addFeature(newFeature)
}

如有转载或 CV 的请标注本站原文地址