实现原理
使用反正切函数实现计算夹角弧度来实现图标的转向。 在开始计算前图标的默认指向角度也要提前了解,如这个 “<-” 默认为180度,正余弦为sin=0 cos=-1夹角弧度为Math.atan2(0, -1)。知道图标的默认弧度后就可实时的计算转向的弧度了,公式为Math.atan2(y2 - y1, x2 - x1) - Math.atan2(0, -1)。
www.shuxuele.com/sine-cosine… 可以使用这个网站来快速查询正弦、余弦值
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css">
<style>
#map {
width: 1200px;
height: 500px;
}
</style>
</head>
<body>
<div id="map"></div>
<label for="speed">
speed:
<input id="speed" type="range" min="10" max="999" step="10" value="60">
</label>
<button id="start-animation">Start Animation</button>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script>
const center = [-5639523.95, -3501274.52];
const map = new ol.Map({
target: document.getElementById('map'),
view: new ol.View({
center: center,
zoom: 10,
minZoom: 2,
maxZoom: 19,
}),
layers: [
new ol.layer.Tile({
name: "OSM",
source: new ol.source.OSM(),
}),
new ol.layer.Tile({
source: new ol.source.TileDebug({
projection: "EPSG:3857",
tileGrid: new ol.source.OSM().getTileGrid()
})
}),
],
controls: ol.control.defaults().extend([
new ol.control.MousePosition({
projection: 'EPSG:3857',
coordinateFormat: function (coordinate) {
// console.log(coordinate)
return coordinate
// return format(coordinate, '经度:{x} 纬度:{y}', 2);
},
}) // 实例化坐标拾取控件,并加入地图
])
});
fetch('https://openlayers.org/en/latest/examples/data/polyline/route.json').then(function (response) {
response.json().then(function (result) {
const polyline = result.routes[0].geometry;
const route = new ol.format.Polyline({
factor: 1e6,
}).readGeometry(polyline, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857',
});
const routeFeature = new ol.Feature({
type: 'route',
geometry: route,
});
const startMarker = new ol.Feature({
type: 'icon',
geometry: new ol.geom.Point(route.getFirstCoordinate()),
});
const endMarker = new ol.Feature({
type: 'icon',
geometry: new ol.geom.Point(route.getLastCoordinate()),
});
const position = startMarker.getGeometry().clone();
const geoMarker = new ol.Feature({
type: 'geoMarker',
geometry: position,
});
// console.log(route.getFirstCoordinate())
const text = new ol.Feature({
// geometry: new ol.geom.Point([-5655881.474053027, -3511517.0817902135]),
geometry: position,
type: 'text',
});
let test = position.getFirstCoordinate()
// 初始样式设置
const styles = {
'route': new ol.style.Style({
stroke: new ol.style.Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
'icon': new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'https://openlayers.org/en/latest/examples/data/icon.png',
}),
}),
text: new ol.style.Style({
text: new ol.style.Text({
text: '->',
// rotateWithView: true,
rotation: Math.atan2(test[0] - test[1], test[1] - test[0]),
font: 'normal 20px 微软雅黑',
fill: new ol.style.Fill({
color: 'red'
})
})
})
};
const vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [routeFeature, geoMarker, startMarker, endMarker, text],
}),
style: function (feature) {
return styles[feature.get('type')];
},
});
map.addLayer(vectorLayer);
const speedInput = document.getElementById('speed');
const startButton = document.getElementById('start-animation');
let animating = false;
let distance = 0;
let lastTime;
let oldP = []
let rotation = 0
function moveFeature(event) {
const speed = Number(speedInput.value);
const time = event.frameState.time;
const elapsedTime = time - lastTime;
distance = (distance + (speed * elapsedTime) / 1e6) % 2;
lastTime = time;
const currentCoordinate = route.getCoordinateAt(
distance > 1 ? 2 - distance : distance
);
position.setCoordinates(currentCoordinate);
const vectorContext = new ol.render.getVectorContext(event);
/**
* 使用反正切函数实现计算夹角弧度来实现图标的转向。
* 在开始计算前图标的默认指向角度也要提前了解
* 如这个 “<-” 默认为180度,正余弦为sin=0 cos=-1夹角弧度为Math.atan2(0, -1)。知道图标的默认弧度后就可实时的计算转向的弧度了
* 公式为Math.atan2(y2 - y1, x2 - x1) - Math.atan2(0, -1)
**/
if (oldP.length) {
rotation = Math.atan2(0, 1) - Math.atan2(currentCoordinate[1] - oldP[1], currentCoordinate[0] - oldP[0])
}
vectorContext.setStyle(new ol.style.Style({
text: new ol.style.Text({
text: '->',
// rotateWithView: true,
rotation: rotation,
font: 'normal 20px 微软雅黑',
fill: new ol.style.Fill({
color: 'red'
})
})
}));
vectorContext.drawGeometry(position);
oldP = currentCoordinate
map.render();
}
function startAnimation() {
animating = true;
lastTime = Date.now();
startButton.textContent = 'Stop Animation';
vectorLayer.on('postrender', moveFeature);
text.setGeometry(null);
}
function stopAnimation() {
animating = false;
startButton.textContent = 'Start Animation';
text.setGeometry(position);
vectorLayer.un('postrender', moveFeature);
}
startButton.addEventListener('click', function () {
if (animating) {
stopAnimation();
} else {
startAnimation();
}
});
});
});
</script>
</body>
</html>