Merge pull request #12463 from MoonE/feature-animation-example
Improve animation examples
This commit is contained in:
@@ -16,8 +16,15 @@ const tileLayer = new TileLayer({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const source = new VectorSource({
|
||||||
|
wrapX: false,
|
||||||
|
});
|
||||||
|
const vector = new VectorLayer({
|
||||||
|
source: source,
|
||||||
|
});
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
layers: [tileLayer],
|
layers: [tileLayer, vector],
|
||||||
target: 'map',
|
target: 'map',
|
||||||
view: new View({
|
view: new View({
|
||||||
center: [0, 0],
|
center: [0, 0],
|
||||||
@@ -26,17 +33,9 @@ const map = new Map({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const source = new VectorSource({
|
|
||||||
wrapX: false,
|
|
||||||
});
|
|
||||||
const vector = new VectorLayer({
|
|
||||||
source: source,
|
|
||||||
});
|
|
||||||
map.addLayer(vector);
|
|
||||||
|
|
||||||
function addRandomFeature() {
|
function addRandomFeature() {
|
||||||
const x = Math.random() * 360 - 180;
|
const x = Math.random() * 360 - 180;
|
||||||
const y = Math.random() * 180 - 90;
|
const y = Math.random() * 170 - 85;
|
||||||
const geom = new Point(fromLonLat([x, y]));
|
const geom = new Point(fromLonLat([x, y]));
|
||||||
const feature = new Feature(geom);
|
const feature = new Feature(geom);
|
||||||
source.addFeature(feature);
|
source.addFeature(feature);
|
||||||
@@ -44,14 +43,18 @@ function addRandomFeature() {
|
|||||||
|
|
||||||
const duration = 3000;
|
const duration = 3000;
|
||||||
function flash(feature) {
|
function flash(feature) {
|
||||||
const start = new Date().getTime();
|
const start = Date.now();
|
||||||
|
const flashGeom = feature.getGeometry().clone();
|
||||||
const listenerKey = tileLayer.on('postrender', animate);
|
const listenerKey = tileLayer.on('postrender', animate);
|
||||||
|
|
||||||
function animate(event) {
|
function animate(event) {
|
||||||
const vectorContext = getVectorContext(event);
|
|
||||||
const frameState = event.frameState;
|
const frameState = event.frameState;
|
||||||
const flashGeom = feature.getGeometry().clone();
|
|
||||||
const elapsed = frameState.time - start;
|
const elapsed = frameState.time - start;
|
||||||
|
if (elapsed >= duration) {
|
||||||
|
unByKey(listenerKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const vectorContext = getVectorContext(event);
|
||||||
const elapsedRatio = elapsed / duration;
|
const elapsedRatio = elapsed / duration;
|
||||||
// radius will be 5 at start and 30 at end.
|
// radius will be 5 at start and 30 at end.
|
||||||
const radius = easeOut(elapsedRatio) * 25 + 5;
|
const radius = easeOut(elapsedRatio) * 25 + 5;
|
||||||
@@ -69,10 +72,6 @@ function flash(feature) {
|
|||||||
|
|
||||||
vectorContext.setStyle(style);
|
vectorContext.setStyle(style);
|
||||||
vectorContext.drawGeometry(flashGeom);
|
vectorContext.drawGeometry(flashGeom);
|
||||||
if (elapsed > duration) {
|
|
||||||
unByKey(listenerKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// tell OpenLayers to continue postrender animation
|
// tell OpenLayers to continue postrender animation
|
||||||
map.render();
|
map.render();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,17 +57,18 @@ fetch('data/polyline/route.json').then(function (response) {
|
|||||||
type: 'route',
|
type: 'route',
|
||||||
geometry: route,
|
geometry: route,
|
||||||
});
|
});
|
||||||
const geoMarker = new Feature({
|
|
||||||
type: 'geoMarker',
|
|
||||||
geometry: new Point(route.getCoordinateAt(0)),
|
|
||||||
});
|
|
||||||
const startMarker = new Feature({
|
const startMarker = new Feature({
|
||||||
type: 'icon',
|
type: 'icon',
|
||||||
geometry: new Point(route.getCoordinateAt(0)),
|
geometry: new Point(route.getFirstCoordinate()),
|
||||||
});
|
});
|
||||||
const endMarker = new Feature({
|
const endMarker = new Feature({
|
||||||
type: 'icon',
|
type: 'icon',
|
||||||
geometry: new Point(route.getCoordinateAt(1)),
|
geometry: new Point(route.getLastCoordinate()),
|
||||||
|
});
|
||||||
|
const position = startMarker.getGeometry().clone();
|
||||||
|
const geoMarker = new Feature({
|
||||||
|
type: 'geoMarker',
|
||||||
|
geometry: position,
|
||||||
});
|
});
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
@@ -95,76 +96,65 @@ fetch('data/polyline/route.json').then(function (response) {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let animating = false;
|
|
||||||
|
|
||||||
const vectorLayer = new VectorLayer({
|
const vectorLayer = new VectorLayer({
|
||||||
source: new VectorSource({
|
source: new VectorSource({
|
||||||
features: [routeFeature, geoMarker, startMarker, endMarker],
|
features: [routeFeature, geoMarker, startMarker, endMarker],
|
||||||
}),
|
}),
|
||||||
style: function (feature) {
|
style: function (feature) {
|
||||||
// hide geoMarker if animation is active
|
|
||||||
if (animating && feature.get('type') === 'geoMarker') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return styles[feature.get('type')];
|
return styles[feature.get('type')];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
map.addLayer(vectorLayer);
|
map.addLayer(vectorLayer);
|
||||||
|
|
||||||
let speed, startTime;
|
|
||||||
const speedInput = document.getElementById('speed');
|
const speedInput = document.getElementById('speed');
|
||||||
const startButton = document.getElementById('start-animation');
|
const startButton = document.getElementById('start-animation');
|
||||||
|
let animating = false;
|
||||||
|
let distance = 0;
|
||||||
|
let lastTime;
|
||||||
|
|
||||||
function moveFeature(event) {
|
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 = getVectorContext(event);
|
const vectorContext = getVectorContext(event);
|
||||||
const frameState = event.frameState;
|
vectorContext.setStyle(styles.geoMarker);
|
||||||
|
vectorContext.drawGeometry(position);
|
||||||
if (animating) {
|
|
||||||
const elapsedTime = frameState.time - startTime;
|
|
||||||
const distance = (speed * elapsedTime) / 1e6;
|
|
||||||
|
|
||||||
if (distance >= 1) {
|
|
||||||
stopAnimation(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPoint = new Point(route.getCoordinateAt(distance));
|
|
||||||
const feature = new Feature(currentPoint);
|
|
||||||
vectorContext.drawFeature(feature, styles.geoMarker);
|
|
||||||
}
|
|
||||||
// tell OpenLayers to continue the postrender animation
|
// tell OpenLayers to continue the postrender animation
|
||||||
map.render();
|
map.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
function startAnimation() {
|
function startAnimation() {
|
||||||
if (animating) {
|
animating = true;
|
||||||
stopAnimation(false);
|
lastTime = Date.now();
|
||||||
} else {
|
startButton.textContent = 'Stop Animation';
|
||||||
animating = true;
|
vectorLayer.on('postrender', moveFeature);
|
||||||
startTime = new Date().getTime();
|
// hide geoMarker and trigger map render through change event
|
||||||
speed = speedInput.value;
|
geoMarker.setGeometry(null);
|
||||||
startButton.textContent = 'Cancel Animation';
|
|
||||||
// hide geoMarker
|
|
||||||
geoMarker.changed();
|
|
||||||
// just in case you pan somewhere else
|
|
||||||
map.getView().setCenter(center);
|
|
||||||
vectorLayer.on('postrender', moveFeature);
|
|
||||||
map.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopAnimation(ended) {
|
function stopAnimation() {
|
||||||
animating = false;
|
animating = false;
|
||||||
startButton.textContent = 'Start Animation';
|
startButton.textContent = 'Start Animation';
|
||||||
|
|
||||||
// if animation cancelled set the marker at the beginning
|
// Keep marker at current animation position
|
||||||
const coord = route.getCoordinateAt(ended ? 1 : 0);
|
geoMarker.setGeometry(position);
|
||||||
geoMarker.getGeometry().setCoordinates(coord);
|
|
||||||
// remove listener
|
|
||||||
vectorLayer.un('postrender', moveFeature);
|
vectorLayer.un('postrender', moveFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
startButton.addEventListener('click', startAnimation, false);
|
startButton.addEventListener('click', function () {
|
||||||
|
if (animating) {
|
||||||
|
stopAnimation();
|
||||||
|
} else {
|
||||||
|
startAnimation();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ function animateFlights(event) {
|
|||||||
|
|
||||||
function addLater(feature, timeout) {
|
function addLater(feature, timeout) {
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
feature.set('start', new Date().getTime());
|
feature.set('start', Date.now());
|
||||||
flightsSource.addFeature(feature);
|
flightsSource.addFeature(feature);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ function updateInfo() {
|
|||||||
|
|
||||||
function setTime() {
|
function setTime() {
|
||||||
startDate.setMinutes(startDate.getMinutes() + 15);
|
startDate.setMinutes(startDate.getMinutes() + 15);
|
||||||
if (startDate > new Date()) {
|
if (startDate > Date.now()) {
|
||||||
startDate = threeHoursAgo();
|
startDate = threeHoursAgo();
|
||||||
}
|
}
|
||||||
layers[1].getSource().updateParams({'TIME': startDate.toISOString()});
|
layers[1].getSource().updateParams({'TIME': startDate.toISOString()});
|
||||||
|
|||||||
Reference in New Issue
Block a user