Анимация маркеров
Вы можете добавлять анимацию к маркерам на карте:
- Движение по окружности.
- Прыжок (движение вверх-вниз).
- Движение по заданным координатам. Дополнительно вы можете добавить CSS-анимацию маркера.
- Анимацию пройденного пути с помощью Polyline.
- Анимацию HTML-маркера с помощью Lottie.
Чтобы добавить анимацию к маркеру, сначала добавьте объект Marker на карту и укажите координаты центра маркера:
const marker = new mapgl.Marker(map, {
coordinates: [55.31878, 25.23584],
});
Движение по окружности
Анимация движения маркера вокруг заданной точки выполняется с помощью изменения координат маркера на окружности. Для этого вычисляются новые координаты на основе текущего времени и угла поворота.
Чтобы добавить анимацию движения по окружности, используйте функцию animateCircle:
function animateCircle(marker, centerCoords, radius, duration) {
const startTime = performance.now();
function frame(time) {
const elapsed = (time - startTime) % duration; // Время с начала текущей итерации анимации
const angle = (2 * Math.PI * elapsed) / duration; // Текущий угол в радианах
const newCoords = [
centerCoords[0] + radius * Math.cos(angle), // Вычисление долготы
centerCoords[1] + radius * Math.sin(angle), // Вычисление широты
];
marker.setCoordinates(newCoords); // Установка новых координат маркера
requestAnimationFrame(frame); // Запуск следующего кадра анимации
}
requestAnimationFrame(frame);
}
// Вызов функции анимации
animateCircle(marker, [55.31878, 25.23584], 0.01, 5000);
Укажите следующие параметры функции animateCircle:
marker: объект маркера, для которого будет добавлена анимация.centerCoords: координаты центра окружности в формате[долгота, широта].radius: радиус окружности в географических координатах (в градусах).duration: длительность одного полного оборота в миллисекундах.
Прыжок
При анимации в виде прыжка маркер двигается вверх и вниз, создавая эффект подпрыгивания.
Для выполнения анимации вычисляется смещение маркера по вертикали относительно базовых координат маркера. Чтобы прыжок выглядел одинаково не зависимо от масштаба карты и корректно при изменении положения камеры, учитывается текущий масштаб карты.
Чтобы добавить анимацию в виде прыжка, используйте функцию animateJump:
function animateJump(marker, map, baseCoords, amplitude, duration) {
const startTime = performance.now();
function frame(time) {
const elapsed = (time - startTime) % duration; // Время с начала текущей итерации анимации
const bounce = Math.sin((2 * Math.PI * elapsed) / duration) * amplitude; // Высота прыжка
// Преобразование географических координат в пиксельные
const basePixelCoords = map.project(baseCoords);
// Добавление смещения по оси Y (по вертикали) в пикселях
const newPixelCoords = [
basePixelCoords[0], // X остаётся без изменений
basePixelCoords[1] - bounce, // Уменьшение Y для прыжка вверх
];
// Преобразование пиксельных координат обратно в географические
const newGeoCoords = map.unproject(newPixelCoords);
// Установка новых координат маркера
marker.setCoordinates(newGeoCoords);
requestAnimationFrame(frame); // Запуск следующего кадра анимации
}
requestAnimationFrame(frame);
}
// Вызов функции анимации
animateJump(marker, map, [55.31878, 25.23584], 20, 1000);
Укажите следующие параметры функции animateJump:
marker: объект маркера, для которого будет добавлена анимация.map: объект карты, используемый для преобразования координат.baseCoords: исходные координаты маркера в формате[долгота, широта].amplitude: амплитуда прыжка (высота движения) в пикселях.duration: длительность одного полного прыжка (вверх и вниз) в миллисекундах.
Движение по координатам
Анимация движения по координатам выполняется с помощью изменения географических координат маркера.
Чтобы добавить анимацию движения по заданному маршруту, используйте функцию animateTravel:
// Функция для интерполяции между двумя точками
function interpolateCoordinates(coord1, coord2, t) {
return [coord1[0] + (coord2[0] - coord1[0]) * t, coord1[1] + (coord2[1] - coord1[1]) * t];
}
function animateTravel(marker, route, durationPerSegment) {
let segmentIndex = 0;
function animateSegment(startTime) {
const elapsedTime = performance.now() - startTime;
const t = elapsedTime / durationPerSegment; // Процент завершения сегмента
if (t < 1) {
// Интерполяция координат
const newCoords = interpolateCoordinates(
route[segmentIndex],
route[segmentIndex + 1],
t,
);
marker.setCoordinates(newCoords);
// Продолжение анимации текущего сегмента
requestAnimationFrame(() => animateSegment(startTime));
} else {
// Переход к следующему сегменту
segmentIndex++;
if (segmentIndex < route.length - 1) {
animateSegment(performance.now());
} else {
// Зацикливание маршрута
segmentIndex = 0;
animateSegment(performance.now());
}
}
}
// Начало анимации первого сегмента
if (route.length > 1) {
animateSegment(performance.now());
}
}
// Вызов функции анимации
const durationPerSegment = 2000;
animateTravel(marker, route, durationPerSegment);
Укажите следующие параметры функции animateTravel:
marker: объект маркера, для которого будет добавлена анимация.route: массив координат маршрута в формате[долгота, широта].durationPerSegment: длительность каждого сегмента в миллисекундах.
Дополнительно вы можете добавить CSS-анимацию маркера.
Движение по координатам с CSS-анимацией
Дополнительно к анимации движения маркера по заданным координатам маршрута вы можете анимировать HTML-маркер с помощью CSS:
-
Создайте CSS-анимацию, например:
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.marker_container {
position: relative;
width: 40px;
height: 40px;
}
.marker_container::before {
content: '';
position: absolute;
top: 10px;
left: 10px;
transform: translate(-50%, -50%);
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(
closest-side,
rgba(255, 165, 0, 0.8),
rgba(255, 69, 0, 0.5),
transparent
);
animation: firePulse 1s infinite ease-in-out;
}
@keyframes firePulse {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.2);
opacity: 0.7;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
.wave {
position: absolute;
top: 10px;
left: 10px;
width: 10px;
height: 10px;
background: rgba(0, 0, 255, 0.5);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: waveAnimation 2s infinite;
}
@keyframes waveAnimation {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(5);
opacity: 0;
}
}
</style> -
Создайте HTML-маркер. Анимация движения по маршруту выполняется с помощью изменения географических координат маркера:
const htmlMarker = new mapgl.HtmlMarker(map, {
coordinates: [55.323, 25.235],
html:
'<div class="marker_container"><div class="wave"></div><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 50 50">\n' +
' <circle cx="25" cy="25" r="25" fill="white"/>\n' +
'</svg></div>\n',
}); -
Добавьте анимацию движения по координатам с помощью функции
animateTravel:// Функция для интерполяции между двумя точками
function interpolateCoordinates(coord1, coord2, t) {
return [coord1[0] + (coord2[0] - coord1[0]) * t, coord1[1] + (coord2[1] - coord1[1]) * t];
}
function animateTravel(htmlMarker, route, durationPerSegment) {
let segmentIndex = 0;
function animateSegment(startTime) {
const elapsedTime = performance.now() - startTime;
const t = elapsedTime / durationPerSegment; // Процент завершения сегмента
if (t < 1) {
// Интерполяция координат
const newCoords = interpolateCoordinates(
route[segmentIndex],
route[segmentIndex + 1],
t,
);
htmlMarker.setCoordinates(newCoords);
// Продолжение анимации текущего сегмента
requestAnimationFrame(() => animateSegment(startTime));
} else {
// Переход к следующему сегменту
segmentIndex++;
if (segmentIndex < route.length - 1) {
animateSegment(performance.now());
} else {
// Зацикливание маршрута
segmentIndex = 0;
animateSegment(performance.now());
}
}
}
// Начало анимации первого сегмента
if (route.length > 1) {
animateSegment(performance.now());
}
}
// Вызов функции анимации
const durationPerSegment = 2000;
animateTravel(htmlMarker, route, durationPerSegment);Укажите следующие параметры функции
animateTravel:htmlMarker: объект HTML-маркера, для которого будет добавлена анимация (создан на шаге 2).route: массив координат маршрута в формате[долгота, широта].durationPerSegment: длительность каждого сегмента в миллисекундах.
Анимация пройденного пути
Вы можете анимировать движение HTML-маркера по заданному маршруту: показать на карте пройденный путь в виде линии и добавить анимацию маркера в виде пульсирующего круга с помощью HTML-элемента Canvas. Для этого:
-
Укажите маршрут маркера в виде массива геокоординат. Чтобы маршрут был замкнутым, конечная координата маркера должна совпадать со стартовой, например:
// Маршрут маркера
const path = [
[37.62372, 55.74965],
[37.61306, 55.74789],
[37.61189, 55.74826],
[37.61097, 55.74889],
[37.6095, 55.74937],
[37.60972, 55.75055],
[37.61114, 55.75259],
[37.61448, 55.75655],
[37.6178, 55.75844],
[37.62453, 55.75953],
[37.62576, 55.7595],
[37.62696, 55.75872],
[37.63023, 55.7566],
[37.63334, 55.75432],
[37.63228, 55.7498],
[37.62812, 55.74995],
[37.62372, 55.74965], // Маршрут замыкается начальной точкой
]; -
Укажите скорость маркера в м/с для каждого сегмента маршрута:
// Скорость в м/с для каждого сегмента
const segmentSpeedsMps = [45, 25, 15, 15, 30, 30, 30, 20, 30, 30, 20, 30, 15, 20, 23, 45]; -
Добавьте функции для вычисления расстояния и длительности сегментов маршрута:
// Функция вычисления длины дуги по геокоординатам в метрах
function getDistance([lon1, lat1], [lon2, lat2]) {
...
}
// Вычисление длительности для каждого сегмента
const segmentDurationsMs = path.slice(0, -1).map((_, i) => {
const dist = getDistance(path[i], path[i + 1]);
const speed = segmentSpeedsMps[i]; // м/с
return (dist / speed) * 1000; // мс
}); -
Для пульсирующего круга создайте
canvas, который используется в качестве HtmlMarkerOptions:// Создание canvas для пульсирующего круга
const canvas = document.createElement('canvas');
canvas.width = 80;
canvas.height = 80;
canvas.style.position = 'absolute';
canvas.style.transform = 'translate(-50%, -50%)';
const ctx = canvas.getContext('2d'); -
Добавьте функцию для отрисовки пульсирующего круга с помощью CanvasRenderingContext2D:
// Функция отрисовки пульсирующего круга
function drawPulse(radius) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(40, 40, radius, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(206,56,56,0.4)';
ctx.fill();
ctx.beginPath();
ctx.arc(40, 40, 14, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(206,56,56,0.8)';
ctx.fill();
}Вместо
canvasвы можете создать анимацию при помощи CSS: см. пример с CSS-анимацией. -
Создайте HTML-маркер, который отображает
canvasна карте:// HTML-маркер с анимированным canvas
const htmlMarker = new mapgl.HtmlMarker(map, {
coordinates: path[0],
html: canvas,
zIndex: 100,
}); -
Добавьте анимацию пройденного пути маркера с помощью полилинии (Polyline) и функции
animate(), которая вызывается при отрисовке кадра:// Функция для интерполяции между двумя точками
function interpolateCoords(a, b, t) {
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
}
let index = 0;
let passedCoords = [path[0]];
let polylines = [];
let startTime = performance.now();
function destroyPolylines() {
polylines.forEach((p) => p.destroy());
polylines = [];
}
function animate() {
// Пульсация круга
const animRadius = 18 + 9 * (0.5 + 0.5 * Math.sin(Date.now() / 250));
drawPulse(animRadius);
// Текущее время на сегменте
const now = performance.now();
const duration = segmentDurationsMs[index];
const t = Math.min((now - startTime) / duration, 1);
const newCoord = interpolateCoords(path[index], path[index + 1], t);
htmlMarker.setCoordinates(newCoord);
// Отрисовка пройденного маршрута с помощью Polyline
const updatedPassed = passedCoords.slice();
updatedPassed.push(newCoord);
destroyPolylines();
polylines.push(
new mapgl.Polyline(map, {
coordinates: updatedPassed,
color: '#ce3838',
width: 5,
}),
);
if (t < 1) {
requestAnimationFrame(animate);
} else {
// Переход к новому сегменту
index++;
if (index >= path.length - 1) {
// В конце маршрута начать сначала
index = 0;
passedCoords = [path[0]];
destroyPolylines();
} else {
passedCoords.push(path[index]);
}
startTime = performance.now();
// Вызов функции animate() при отрисовке кадра
requestAnimationFrame(animate);
}
}
// Вызов функции анимации
animate();
Анимация с помощью Lottie
Вы можете анимировать HTML-маркер с помощью Lottie:
-
Подключите Lottie:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.0/lottie.min.js"></script> -
Создайте HTML-маркер:
const markerHtml = `
<div class="marker_container">
<div class="lottie_animation" id="lottie"></div>
</div>
`;
const htmlMarker = new mapgl.HtmlMarker(map, {
coordinates: [55.280324, 25.249851],
html: markerHtml,
}); -
Создайте анимацию с помощью Lottie:
// Используем MutationObserver для отслеживания появления элемента
// Вместо этого можно использовать setTimeout со значением 0
const observer = new MutationObserver(() => {
const lottieContainer = document.getElementById('lottie');
if (lottieContainer) {
observer.disconnect(); // Отключаем наблюдение, когда элемент найден
lottie.loadAnimation({
container: lottieContainer, // ID контейнера
renderer: 'svg',
loop: true,
autoplay: true,
path: '<your>.json', // Путь к JSON
});
}
});
observer.observe(document.body, { childList: true, subtree: true });