Анимация маркеров
Вы можете добавлять анимацию к маркерам на карте:
- Движение по окружности.
- Прыжок (движение вверх-вниз).
- Движение по заданным координатам. Дополнительно вы можете добавить CSS-анимацию маркера.
- Анимацию пройденного пути с помощью Polyline.
- Анимацию HTML-маркера с помощью Lottie.
Чтобы добавить анимацию к маркеру, сначала добавьте объект Marker на карту и укажите координаты центра маркера:
const marker = new mapgl.Marker(map, {
coordinates: [37.615655, 55.768005],
});
Движение по окружности
Анимация движения маркера вокруг заданной точки выполняется с помощью изменения координат маркера на окружности. Для этого вычисляются новые координаты на основе текущего времени и угла поворота.
Чтобы добавить анимацию движения по окружности, используйте функцию 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, [37.615655, 55.768005], 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, [37.615655, 55.768005], 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: [37.615655, 55.768005],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.60950, 55.74937],[37.60972, 55.75055],[37.61114, 55.75259],[37.61448, 55.75655],[37.61780, 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-маркер с анимированным canvasconst 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);// Отрисовка пройденного маршрута с помощью Polylineconst 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: [37.615655, 55.768005],html: markerHtml,}); -
Создайте анимацию с помощью Lottie:
// Используем MutationObserver для отслеживания появления элемента// Вместо этого можно использовать setTimeout со значением 0const 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 });