Skip to content

路径行走动画

上次更新 2025年8月20日星期三 3:16:20 字数 0 字 时长 0 分钟

本章将介绍如何在 OpenLayers 中实现路径行走动画效果。

示例

新标签页预览

代码实现

vue
<template>
  <div class="h-screen">
    <div id="map" class="w-full h-full"></div>
    <button
      id="startButton"
      class="control-button"
      :disabled="isAnimating"
      @click="startAnimation"
    >
      {{ buttonText }}
    </button>
  </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
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 VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
import LineString from "ol/geom/LineString";
import Point from "ol/geom/Point";
import { Style, Stroke, Icon } from "ol/style";
import { fromLonLat } from "ol/proj";

const isAnimating = ref(false);
const buttonText = ref("开始动画");

onMounted(() => {
  // 创建地图
  const map = new Map({
    target: "map",
    view: new View({
      center: fromLonLat([117.22686767578126, 39.09361883285121]), // 天津
      zoom: 12,
    }),
  });

  // 添加天地图图层
  const tdtLayer = new TileLayer({
    source: new XYZ({
      url: "http://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=d32c6748c80f81a44acd8633cea41dfd",
    }),
  });
  map.addLayer(tdtLayer);

  // 定义路径点
  const pathCoordinates = [
    [117.20083, 39.084899], // 起点(左下角)
    [117.20083, 39.104899], // 左下到左上
    [117.25083, 39.104899], // 左上到右上
    [117.25083, 39.084899], // 右上到右下
    [117.23083, 39.086899], // 右下往左
    [117.23083, 39.102899], // 向上
    [117.22083, 39.102899], // 向左
    [117.22083, 39.088899], // 向下
    [117.21083, 39.088899], // 继续向下
    [117.21083, 39.094899], // 终点
  ].map((coord) => fromLonLat(coord));

  // 创建路径线要素
  const lineString = new LineString(pathCoordinates);
  const feature = new Feature({
    geometry: lineString,
  });

  // 创建矢量图层
  const vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: [feature],
    }),
    style: new Style({
      stroke: new Stroke({
        color: "blue",
        width: 3,
      }),
    }),
  });
  map.addLayer(vectorLayer);

  // 创建标记要素
  const markerFeature = new Feature({
    geometry: new Point(pathCoordinates[0]),
  });

  // 创建标记图层
  const markerLayer = new VectorLayer({
    source: new VectorSource({
      features: [markerFeature],
    }),
    style: new Style({
      image: new Icon({
        src: "./icon.svg",
        scale: 0.5,
      }),
    }),
  });
  map.addLayer(markerLayer);

  let currentAnimation = null;
  let i = 0;

  // 动画函数
  function animate(duration = 2000, frames = 120) {
    isAnimating.value = true;
    buttonText.value = "动画进行中...";

    const avgTime = duration / (pathCoordinates.length - 1) / frames;

    if (i < pathCoordinates.length - 1) {
      const start = pathCoordinates[i];
      const end = pathCoordinates[i + 1];
      let frame = 0;

      currentAnimation = setInterval(() => {
        frame++;
        const progress = frame / frames;

        const x = start[0] + (end[0] - start[0]) * progress;
        const y = start[1] + (end[1] - start[1]) * progress;

        markerFeature.getGeometry().setCoordinates([x, y]);

        if (frame === frames) {
          clearInterval(currentAnimation);
          i++;
          animate();
        }
      }, avgTime);
    } else {
      // 动画结束,重置状态
      i = 0;
      isAnimating.value = false;
      buttonText.value = "开始动画";
      markerFeature.getGeometry().setCoordinates(pathCoordinates[0]);
    }
  }

  // 开始动画
  const startAnimation = () => {
    markerFeature.getGeometry().setCoordinates(pathCoordinates[0]);
    i = 0;
    animate(1000);
  };
});
</script>

<style scoped>
.h-screen {
  height: 24rem;
}

.w-full {
  width: 100%;
}

.h-full {
  height: 100%;
}

.control-button {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 10px 20px;
  background-color: #4caf50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  transition: all 0.3s ease;
  z-index: 1000;
}

.control-button:hover {
  background-color: #45a049;
}

.control-button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}
</style>
关注公众号