路径行走动画
本章将介绍如何在 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>