4 changed files with 4 additions and 364 deletions
@ -1,360 +0,0 @@ |
|||||
<!doctype html> |
|
||||
<html lang="zh"> |
|
||||
<head> |
|
||||
<meta charset="utf-8" /> |
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
||||
<title>轨迹点实现</title> |
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol/ol.css" /> |
|
||||
<style> |
|
||||
#map { |
|
||||
width: 100%; |
|
||||
height: 500px; |
|
||||
} |
|
||||
#controls { |
|
||||
padding: 10px; |
|
||||
background: #f5f5f5; |
|
||||
} |
|
||||
#info-panel { |
|
||||
padding: 10px; |
|
||||
background: white; |
|
||||
border: 1px solid #ddd; |
|
||||
} |
|
||||
.timeline { |
|
||||
display: flex; |
|
||||
overflow-x: auto; |
|
||||
padding: 10px 0; |
|
||||
} |
|
||||
.timeline-marker { |
|
||||
padding: 5px 10px; |
|
||||
margin: 0 5px; |
|
||||
background: #1890ff; |
|
||||
color: white; |
|
||||
border-radius: 3px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
</style> |
|
||||
</head> |
|
||||
<body> |
|
||||
<div id="controls"> |
|
||||
<button id="btn-play">播放轨迹</button> |
|
||||
<button id="btn-pause">暂停</button> |
|
||||
<div id="info-panel">轨迹信息将显示在这里</div> |
|
||||
</div> |
|
||||
<div id="map"></div> |
|
||||
<div class="timeline" id="timeline"></div> |
|
||||
<script src="https://unpkg.com/ol@9.2.4/dist/ol.js"></script> |
|
||||
<script> |
|
||||
// 轨迹数据(示例数据 - 北京到天津的轨迹) |
|
||||
const trackData = [ |
|
||||
{ time: '09:00', lng: 116.404, lat: 39.915, speed: 60, name: '起点' }, |
|
||||
{ time: '09:15', lng: 116.704, lat: 39.915, speed: 65, name: '大兴区' }, |
|
||||
{ time: '09:30', lng: 116.904, lat: 39.815, speed: 70, name: '廊坊' }, |
|
||||
{ time: '09:45', lng: 117.204, lat: 39.715, speed: 75, name: '武清区' }, |
|
||||
{ time: '10:00', lng: 117.404, lat: 39.615, speed: 80, name: '天津市区' } |
|
||||
] |
|
||||
|
|
||||
// 全局变量 |
|
||||
let map, trackSource, trackPlayer |
|
||||
|
|
||||
// 初始化地图 |
|
||||
function initMap() { |
|
||||
const source = new ol.source.XYZ({ |
|
||||
url: 'http://qtbj.icpcdev.site/roadmap/{z}/{x}/{y}.png', |
|
||||
maxZoom: 16, |
|
||||
minZoom: 10 |
|
||||
}) |
|
||||
|
|
||||
map = new ol.Map({ |
|
||||
target: 'map', |
|
||||
layers: [ |
|
||||
new ol.layer.Tile({ |
|
||||
source: source |
|
||||
}) |
|
||||
], |
|
||||
view: new ol.View({ |
|
||||
center: ol.proj.fromLonLat([116.404, 39.915]), |
|
||||
zoom: 10 |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
// 创建轨迹图层 |
|
||||
trackSource = new ol.source.Vector() |
|
||||
const trackLayer = new ol.layer.Vector({ |
|
||||
source: trackSource, |
|
||||
style: function (feature) { |
|
||||
const type = feature.get('type') |
|
||||
if (type === 'track-line') { |
|
||||
return new ol.style.Style({ |
|
||||
stroke: new ol.style.Stroke({ |
|
||||
color: '#1890FF', |
|
||||
width: 4, |
|
||||
lineDash: [5, 5] |
|
||||
}) |
|
||||
}) |
|
||||
} else if (type === 'track-point') { |
|
||||
const speed = feature.get('speed') |
|
||||
return new ol.style.Style({ |
|
||||
image: new ol.style.Circle({ |
|
||||
radius: 6, |
|
||||
fill: new ol.style.Fill({ |
|
||||
color: speed > 70 ? '#FF4D4F' : '#52C41A' |
|
||||
}), |
|
||||
stroke: new ol.style.Stroke({ |
|
||||
color: 'white', |
|
||||
width: 2 |
|
||||
}) |
|
||||
}), |
|
||||
text: new ol.style.Text({ |
|
||||
text: feature.get('name') || feature.get('time'), |
|
||||
offsetY: -15, |
|
||||
fill: new ol.style.Fill({ color: '#000' }), |
|
||||
stroke: new ol.style.Stroke({ color: '#fff', width: 3 }) |
|
||||
}) |
|
||||
}) |
|
||||
} else if (type === 'moving-point') { |
|
||||
return new ol.style.Style({ |
|
||||
image: new ol.style.Circle({ |
|
||||
radius: 10, |
|
||||
fill: new ol.style.Fill({ color: '#FAAD14' }), |
|
||||
stroke: new ol.style.Stroke({ |
|
||||
color: 'white', |
|
||||
width: 3 |
|
||||
}) |
|
||||
}) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
map.addLayer(trackLayer) |
|
||||
} |
|
||||
|
|
||||
// 轨迹播放器类 |
|
||||
class TrackPlayer { |
|
||||
constructor(trackData, source, map) { |
|
||||
this.trackData = trackData |
|
||||
this.source = source |
|
||||
this.map = map |
|
||||
this.currentIndex = 0 |
|
||||
this.isPlaying = false |
|
||||
this.animationId = null |
|
||||
this.movingPoint = null |
|
||||
|
|
||||
this.initMovingPoint() |
|
||||
this.createTimeline() |
|
||||
} |
|
||||
|
|
||||
// 初始化移动点 |
|
||||
initMovingPoint() { |
|
||||
this.movingPoint = new ol.Feature({ |
|
||||
geometry: new ol.geom.Point( |
|
||||
ol.proj.fromLonLat([this.trackData[0].lng, this.trackData[0].lat]) |
|
||||
), |
|
||||
type: 'moving-point' |
|
||||
}) |
|
||||
this.source.addFeature(this.movingPoint) |
|
||||
} |
|
||||
|
|
||||
// 创建时间轴 |
|
||||
createTimeline() { |
|
||||
const timeline = document.getElementById('timeline') |
|
||||
timeline.innerHTML = '' |
|
||||
|
|
||||
this.trackData.forEach((point, index) => { |
|
||||
const pointElement = document.createElement('div') |
|
||||
pointElement.className = 'timeline-point' |
|
||||
pointElement.innerHTML = ` |
|
||||
<div>${point.time}</div> |
|
||||
<div>${point.name}</div> |
|
||||
` |
|
||||
pointElement.addEventListener('click', () => { |
|
||||
this.jumpToPoint(index) |
|
||||
}) |
|
||||
timeline.appendChild(pointElement) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// 播放轨迹 |
|
||||
play() { |
|
||||
if (this.isPlaying) return |
|
||||
this.isPlaying = true |
|
||||
this.currentIndex = 0 |
|
||||
this.updateTimeline() |
|
||||
this.animate() |
|
||||
} |
|
||||
|
|
||||
// 暂停播放 |
|
||||
pause() { |
|
||||
this.isPlaying = false |
|
||||
if (this.animationId) { |
|
||||
cancelAnimationFrame(this.animationId) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 跳转到指定点 |
|
||||
jumpToPoint(index) { |
|
||||
this.pause() |
|
||||
this.currentIndex = Math.max(0, Math.min(index, this.trackData.length - 1)) |
|
||||
this.updateMovingPoint() |
|
||||
this.updateTimeline() |
|
||||
} |
|
||||
|
|
||||
// 动画循环 |
|
||||
animate() { |
|
||||
if (!this.isPlaying || this.currentIndex >= this.trackData.length - 1) { |
|
||||
this.isPlaying = false |
|
||||
this.updateInfo('轨迹播放完成') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
const start = this.trackData[this.currentIndex] |
|
||||
const end = this.trackData[this.currentIndex + 1] |
|
||||
this.moveToNextPoint(start, end, 0, 1500) // 1.5秒移动到下一点 |
|
||||
} |
|
||||
|
|
||||
// 移动到下一点 |
|
||||
moveToNextPoint(start, end, progress, duration) { |
|
||||
if (!this.isPlaying) return |
|
||||
|
|
||||
if (progress >= 1) { |
|
||||
this.currentIndex++ |
|
||||
this.updateTimeline() |
|
||||
this.animate() |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 计算插值坐标 |
|
||||
const lng = start.lng + (end.lng - start.lng) * progress |
|
||||
const lat = start.lat + (end.lat - start.lat) * progress |
|
||||
|
|
||||
// 更新移动点位置 |
|
||||
this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([lng, lat])) |
|
||||
|
|
||||
// 更新信息面板 |
|
||||
this.updateInfo(end, progress) |
|
||||
|
|
||||
// 平滑跟随移动点 |
|
||||
if (progress > 0.3) { |
|
||||
// 延迟一点开始跟随 |
|
||||
this.map.getView().animate({ |
|
||||
center: ol.proj.fromLonLat([lng, lat]), |
|
||||
duration: 200, |
|
||||
easing: ol.easing.linear |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// 继续动画 |
|
||||
progress += 16 / duration // 基于60fps |
|
||||
this.animationId = requestAnimationFrame(() => { |
|
||||
this.moveToNextPoint(start, end, progress, duration) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// 更新移动点位置(直接跳转) |
|
||||
updateMovingPoint() { |
|
||||
const point = this.trackData[this.currentIndex] |
|
||||
this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([point.lng, point.lat])) |
|
||||
|
|
||||
this.map.getView().animate({ |
|
||||
center: ol.proj.fromLonLat([point.lng, point.lat]), |
|
||||
zoom: 14, |
|
||||
duration: 500 |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// 更新信息面板 |
|
||||
updateInfo(point, progress) { |
|
||||
const progressText = progress ? `进度: ${Math.round(progress * 100)}%` : '' |
|
||||
document.getElementById('info-panel').innerHTML = ` |
|
||||
<div><strong>当前位置:</strong> ${point.name}</div> |
|
||||
<div><strong>时间:</strong> ${point.time}</div> |
|
||||
<div><strong>速度:</strong> ${point.speed} km/h</div> |
|
||||
<div><strong>${progressText}</strong></div> |
|
||||
` |
|
||||
} |
|
||||
|
|
||||
// 更新时间轴高亮 |
|
||||
updateTimeline() { |
|
||||
const points = document.querySelectorAll('.timeline-point') |
|
||||
points.forEach((point, index) => { |
|
||||
point.classList.toggle('active', index === this.currentIndex) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// 重置轨迹 |
|
||||
reset() { |
|
||||
this.pause() |
|
||||
this.currentIndex = 0 |
|
||||
this.updateMovingPoint() |
|
||||
this.updateTimeline() |
|
||||
this.updateInfo(this.trackData[0], 0) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 添加轨迹到地图 |
|
||||
function addTrackToMap() { |
|
||||
// 清空现有轨迹 |
|
||||
trackSource.clear() |
|
||||
|
|
||||
// 添加轨迹点 |
|
||||
trackData.forEach((point, index) => { |
|
||||
const feature = new ol.Feature({ |
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([point.lng, point.lat])), |
|
||||
type: 'track-point', |
|
||||
time: point.time, |
|
||||
speed: point.speed, |
|
||||
name: point.name, |
|
||||
index: index |
|
||||
}) |
|
||||
trackSource.addFeature(feature) |
|
||||
}) |
|
||||
|
|
||||
// 添加轨迹线 |
|
||||
const lineCoords = trackData.map((p) => ol.proj.fromLonLat([p.lng, p.lat])) |
|
||||
const lineFeature = new ol.Feature({ |
|
||||
geometry: new ol.geom.LineString(lineCoords), |
|
||||
type: 'track-line' |
|
||||
}) |
|
||||
trackSource.addFeature(lineFeature) |
|
||||
|
|
||||
// 自适应缩放 |
|
||||
const extent = trackSource.getExtent() |
|
||||
map.getView().fit(extent, { |
|
||||
padding: [50, 50, 50, 50], |
|
||||
duration: 1000 |
|
||||
}) |
|
||||
|
|
||||
// 初始化播放器 |
|
||||
trackPlayer = new TrackPlayer(trackData, trackSource, map) |
|
||||
} |
|
||||
|
|
||||
// 页面加载完成后初始化 |
|
||||
document.addEventListener('DOMContentLoaded', function () { |
|
||||
// 初始化地图 |
|
||||
initMap() |
|
||||
|
|
||||
// 绑定按钮事件 |
|
||||
// document.getElementById('btn-add-track').addEventListener('click', addTrackToMap); |
|
||||
document.getElementById('btn-play').addEventListener('click', function () { |
|
||||
if (trackPlayer) trackPlayer.play() |
|
||||
}) |
|
||||
|
|
||||
// document.getElementById('btn-play') |
|
||||
// document.getElementById('btn-pause').addEventListener('click', function() { |
|
||||
// if (trackPlayer) trackPlayer.pause(); |
|
||||
// }); |
|
||||
// document.getElementById('btn-reset').addEventListener('click', function() { |
|
||||
// if (trackPlayer) trackPlayer.reset(); |
|
||||
// }); |
|
||||
// document.getElementById('btn-clear').addEventListener('click', function() { |
|
||||
// trackSource.clear(); |
|
||||
// document.getElementById('timeline').innerHTML = ''; |
|
||||
// document.getElementById('info-panel').innerHTML = '轨迹已清空'; |
|
||||
// }); |
|
||||
// trackPlayer.play(); |
|
||||
// 页面加载后自动添加示例轨迹 |
|
||||
setTimeout(addTrackToMap, 1500) |
|
||||
}) |
|
||||
</script> |
|
||||
</body> |
|
||||
</html> |
|
||||
Loading…
Reference in new issue