Анимация маркеров
Вы можете добавлять анимацию к маркерам на карте:
- движение по окружности вокруг точки,
- прыжок (движение вверх-вниз),
- движение по маршруту (по заданным географическим координатам).
Также вы можете анимировать HTML-маркер при помощи CSS или 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
: длительность одного полного оборота в миллисекундах.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2GIS Map API</title>
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const map = new mapgl.Map('container', {
center: [55.323, 25.235],
zoom: 10.5,
key: 'Your API access key',
});
const marker = new mapgl.Marker(map, {
coordinates: [55.323, 25.235],
});
const centerCoords = [55.323, 25.235];
const radius = 0.1;
const duration = 5000;
function animateMarkerInCircle(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);
}
animateMarkerInCircle(marker, centerCoords, radius, duration);
</script>
</body>
</html>
Прыжок
При анимации в виде прыжка маркер двигается вверх и вниз, создавая эффект подпрыгивания.
Для выполнения анимации вычисляется смещение маркера по вертикали относительно базовых координат маркера. Чтобы прыжок выглядел одинаково не зависимо от масштаба карты и корректно при изменении положения камеры, учитывается текущий масштаб карты.
Чтобы добавить анимацию в виде прыжка, используйте функцию 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
: длительность одного полного прыжка (вверх и вниз) в миллисекундах.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2GIS Map API</title>
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const map = new mapgl.Map('container', {
center: [55.323, 25.235],
zoom: 10.5,
key: 'Your API access key',
});
const marker = new mapgl.Marker(map, {
coordinates: [55.323, 25.235],
});
const centerCoords = [55.323, 25.235];
const radius = 0.1;
const duration = 5000;
function jumpMarkerAnimation(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);
const newPixelCoords = [
basePixelCoords[0],
basePixelCoords[1] - bounce,
];
const newGeoCoords = map.unproject(newPixelCoords);
marker.setCoordinates(newGeoCoords);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
jumpMarkerAnimation(marker, map, [55.323, 25.235], 20, 1000);
</script>
</body>
</html>
Движение по координатам
Анимация движения по маршруту выполняется с помощью изменения географических координат маркера.
Чтобы добавить анимацию движения по координатам, используйте функцию 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
: длительность каждого сегмента в миллисекундах.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2GIS Map API - Courier Animation</title>
<style>
html,
body,
#container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const map = new mapgl.Map('container', {
center: [55.280, 25.249],
zoom: 14,
key: 'Your API access key',
});
const route = `55.280324 25.249851,55.272991 25.239421,55.278568 25.235182,55.284862 25.246507,55.280324 25.249851`.split(',')
.map(pair => pair.split(' ').map(Number));
const marker = new mapgl.Marker(map, {
coordinates: route[0],
icon: 'https://disk.2gis.com/styles/assets/icons/poi_cars-66605ef515e3adca7dea4e3f1bac00195e250a1250a5bf9e313708cb25fd1467.svg',
size: [20,20],
});
function interpolateCoordinates(coord1, coord2, t) {
return [
coord1[0] + (coord2[0] - coord1[0]) * t,
coord1[1] + (coord2[1] - coord1[1]) * t,
];
}
function animateCourier(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;
animateCourier(marker, route, durationPerSegment);
window.addEventListener('unload', () => map.destroy());
</script>
</body>
</html>
Анимация движения при помощи CSS
Вы можете анимировать движение HTML-маркера при помощи CSS. По примеру ниже вы сможете анимировать движение HTML-маркера по маршруту.
-
Создайте 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
: длительность каждого сегмента в миллисекундах.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2GIS Map API - Courier CSS Animation</title>
<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>
</head>
<body>
<div id="container"></div>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const map = new mapgl.Map('container', {
center: [55.280, 25.249],
zoom: 14,
key: 'Your API access key',
});
const route = `55.280324 25.249851,55.272991 25.239421,55.278568 25.235182,55.283162 25.246507,55.280324 25.249851`.split(',')
.map(pair => pair.split(' ').map(Number));
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',
});
function interpolateCoordinates(coord1, coord2, t) {
return [
coord1[0] + (coord2[0] - coord1[0]) * t,
coord1[1] + (coord2[1] - coord1[1]) * t,
];
}
function animateCourier(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;
animateCourier(htmlMarker, route, durationPerSegment);
window.addEventListener('unload', () => map.destroy());
</script>
</body>
</html>
Анимация маркера с помощью 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 });
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.0/lottie.min.js"></script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2GIS Map API - Lottie Animation</title>
<style>
html, body, #container {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.marker_container {
right: 20px;
bottom: 20px;
position: relative;
width: 40px;
height: 40px;
}
.lottie_animation {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://mapgl.2gis.com/api/js/v1"></script>
<script>
const map = new mapgl.Map('container', {
center: [55.280, 25.249],
zoom: 14,
key: 'Your API access key',
});
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,
});
const observer = new MutationObserver(() => {
const lottieContainer = document.getElementById('lottie');
if (lottieContainer) {
observer.disconnect();
lottie.loadAnimation({
container: lottieContainer,
renderer: 'svg',
loop: true,
autoplay: true,
path: 'https://lottie.host/7401522f-2d8b-4049-ad18-eb0edb6af224/CE9lFrNlEH.json',
});
}
});
observer.observe(document.body, { childList: true, subtree: true });
window.addEventListener('unload', () => map.destroy());
</script>
</body>
</html>