点动画
通过每帧更新GeoJSON源来制作点位置动画;
<!DOCTYPE html>
<html lang="en">
<head>
<title>点动画</title>
<meta property="og:description" content="通过每帧更新GeoJSON源来制作点位置动画" />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.js'></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>
<body>
<style>
.overlay {
position: absolute;
top: 10px;
left: 10px;
background: white;
padding: 5px;
font-family: Arial, sans-serif;
}
#reset {
position: absolute;
bottom: 30px;
left: 10px;
}
</style>
<div id="map"></div>
<div class="overlay">
<div><strong>点位置:</strong> <span id="location">[0, 0]</span></div>
</div>
<button id="reset">重置动画</button>
<script>
const map = new maplibregl.Map({
container: 'map',
style:
'https://demotiles.maplibre.org/styles/osm-bright-gl-style/style.json',
center: [0, 0],
zoom: 0.5
});
// 圆点的位置源
const geojson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'Point',
'coordinates': [0, 0]
}
}
]
};
// 动画和路径变量
const path = []; // 储存路径坐标
const pathDistance = 700; // 每条路径的总距离
let startTime = 0;
let frame = 0;
let playing = true;
let pointX = 0;
let pointY = 0;
const locationDisplay = document.getElementById('location');
const resetButton = document.getElementById('reset');
// 随机路径生成函数
function generatePath() {
// 生成一个花卉图形路径
path.length = 0; // 清空现有路径
const amplitude = 40; // 振幅
const numLoops = 4; // 环数
const loopDelta = (Math.PI * 2) / numLoops;
for (let a = 0; a < Math.PI * 16; a += 0.1) {
// 创建一个曲线
const r = amplitude * Math.sin(a * numLoops) + 80;
// 转换为笛卡尔坐标
const x = r * Math.cos(a);
const y = r * Math.sin(a);
// 添加到路径数组
path.push([x, y]);
}
}
map.on('load', () => {
// 生成初始路径
generatePath();
// 添加圆点源
map.addSource('point', {
'type': 'geojson',
'data': geojson
});
// 添加一个线层显示路径
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': path
}
}
});
// 添加路径线
map.addLayer({
'id': 'route',
'source': 'route',
'type': 'line',
'paint': {
'line-width': 2,
'line-color': '#007cbf',
'line-opacity': 0.5
}
});
// 添加点层
map.addLayer({
'id': 'point',
'source': 'point',
'type': 'circle',
'paint': {
'circle-radius': 10,
'circle-color': '#ff0000'
}
});
startTime = performance.now();
animatePoint();
// 重置按钮
resetButton.addEventListener('click', () => {
generatePath();
frame = 0;
startTime = performance.now();
playing = true;
// 更新路径
map.getSource('route').setData({
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': path
}
});
});
});
function animatePoint() {
if (playing) {
// 绘制一个点沿着路径移动
const timestamp = performance.now();
// 计算动画速度,重复路径
const elapsed = (timestamp - startTime) / 20;
frame = elapsed % path.length;
if (path.length > 0) {
// 获取当前位置
const position = Math.floor(frame);
if (position < path.length) {
pointX = path[position][0];
pointY = path[position][1];
geojson.features[0].geometry.coordinates = [pointX, pointY];
// 更新源
map.getSource('point').setData(geojson);
// 更新位置显示
locationDisplay.textContent = `[${pointX.toFixed(2)}, ${pointY.toFixed(2)}]`;
}
}
}
// 请求下一帧
requestAnimationFrame(animatePoint);
}
</script>
</body>
</html>