Анимация маркеров в Google Maps JavaScript API применяется для привлечения внимания к объектам на карте, визуального отображения изменений состояния, индикации новых событий и повышения удобства взаимодействия с картографическим интерфейсом.
Анимированные маркеры позволяют:
Google Maps JavaScript API предоставляет встроенные механизмы анимации, а также позволяет реализовывать собственные анимационные эффекты средствами JavaScript и CSS.
API содержит перечисление google.maps.Animation, которое
предоставляет два стандартных эффекта:
| Анимация | Описание |
|---|---|
DROP |
Плавное падение маркера сверху карты |
BOUNCE |
Подпрыгивание маркера на месте |
Эти эффекты работают только с объектами класса
google.maps.Marker.
Эффект DROP создаёт впечатление, что маркер падает на карту сверху.
const marker = new google.maps.Marker({
position: {
lat: 55.7558,
lng: 37.6176
},
map: map,
animation: google.maps.Animation.DROP
});
После создания маркер плавно появится на карте.
Анимацию можно включить позже.
const marker = new google.maps.Marker({
position: {
lat: 55.7558,
lng: 37.6176
},
map: map
});
marker.setAnimation(
google.maps.Animation.DROP
);
Такой подход удобен при динамическом добавлении точек после загрузки карты.
Эффект BOUNCE заставляет маркер непрерывно подпрыгивать.
const marker = new google.maps.Marker({
position: {
lat: 55.7558,
lng: 37.6176
},
map: map,
animation: google.maps.Animation.BOUNCE
});
После отображения маркер будет постоянно двигаться вверх и вниз.
Для отключения анимации используется значение null.
marker.setAnimation(null);
Полный пример:
const marker = new google.maps.Marker({
position: {
lat: 55.7558,
lng: 37.6176
},
map: map,
animation: google.maps.Animation.BOUNCE
});
setTimeout(() => {
marker.setAnimation(null);
}, 3000);
Через три секунды подпрыгивание прекратится.
Часто используется сценарий, при котором пользователь нажимает на маркер для его выделения.
marker.addListener("click", () => {
if (marker.getAnimation() !== null) {
marker.setAnimation(null);
} else {
marker.setAnimation(
google.maps.Animation.BOUNCE
);
}
});
После каждого нажатия состояние анимации меняется.
При работе со списками объектов полезно анимировать только активный маркер.
let activeMarker = null;
function selectMarker(marker) {
if (activeMarker) {
activeMarker.setAnimation(null);
}
marker.setAnimation(
google.maps.Animation.BOUNCE
);
activeMarker = marker;
}
Использование:
markers.forEach(marker => {
marker.addListener("click", () => {
selectMarker(marker);
});
});
В каждый момент времени анимируется только один объект.
Постоянное подпрыгивание может отвлекать пользователя. Намного удобнее запускать эффект на короткое время.
marker.addListener("click", () => {
marker.setAnimation(
google.maps.Animation.BOUNCE
);
setTimeout(() => {
marker.setAnimation(null);
}, 1500);
});
Такой подход используется чаще всего.
При получении объектов с сервера можно визуально показать их появление.
fetch("/api/places")
.then(response => response.json())
.then(data => {
data.forEach(place => {
new google.maps.Marker({
position: place.position,
map: map,
animation:
google.maps.Animation.DROP
});
});
});
Каждая новая точка будет плавно появляться на карте.
Иногда необходимо создать эффект постепенного заполнения карты.
locations.forEach((location, index) => {
setTimeout(() => {
new google.maps.Marker({
position: location,
map: map,
animation:
google.maps.Animation.DROP
});
}, index * 300);
});
Каждый следующий маркер появляется через 300 миллисекунд после предыдущего.
Для сотен маркеров одновременное использование анимации может негативно влиять на производительность.
Лучше применять интервалы появления:
for (let i = 0; i < locations.length; i++) {
setTimeout(() => {
createMarker(locations[i]);
}, i * 100);
}
Так уменьшается нагрузка на браузер и повышается плавность интерфейса.
Стандартный API не содержит встроенной анимации перемещения. Для плавного движения необходимо изменять координаты вручную.
const start = {
lat: 55.7558,
lng: 37.6176
};
const end = {
lat: 55.7658,
lng: 37.6276
};
const marker = new google.maps.Marker({
position: start,
map: map
});
function animateMarker(marker, start, end) {
const steps = 100;
let currentStep = 0;
const interval = setInterval(() => {
currentStep++;
const lat =
start.lat +
(end.lat - start.lat) *
currentStep / steps;
const lng =
start.lng +
(end.lng - start.lng) *
currentStep / steps;
marker.setPosition({
lat,
lng
});
if (currentStep >= steps) {
clearInterval(interval);
}
}, 20);
}
Запуск:
animateMarker(marker, start, end);
Получается плавное движение между двумя точками.
Для более качественной анимации рекомендуется применять
requestAnimationFrame.
function moveMarker(marker, start, end, duration) {
const startTime = performance.now();
function animate(currentTime) {
const elapsed =
currentTime - startTime;
const progress =
Math.min(elapsed / duration, 1);
const lat =
start.lat +
(end.lat - start.lat) *
progress;
const lng =
start.lng +
(end.lng - start.lng) *
progress;
marker.setPosition({
lat,
lng
});
if (progress < 1) {
requestAnimationFrame(
animate
);
}
}
requestAnimationFrame(animate);
}
Преимущества:
Частая задача — отображение такси, автобусов, курьеров и других подвижных объектов.
setInterval(async () => {
const response =
await fetch("/vehicle");
const position =
await response.json();
marker.setPosition(position);
}, 5000);
Однако резкое изменение координат выглядит неестественно.
Лучше использовать промежуточную анимацию:
animateMarker(
marker,
marker.getPosition().toJSON(),
position
);
Тогда объект будет двигаться плавно.
Если имеется массив координат, маркер можно перемещать последовательно между ними.
const route = [
{ lat: 55.75, lng: 37.61 },
{ lat: 55.76, lng: 37.62 },
{ lat: 55.77, lng: 37.63 }
];
Функция движения:
let index = 0;
function moveNext() {
if (index >= route.length - 1) {
return;
}
moveMarker(
marker,
route[index],
route[index + 1],
1000
);
index++;
setTimeout(moveNext, 1000);
}
Запуск:
moveNext();
Маркер будет проходить по всем точкам маршрута.
Иногда достаточно визуально изменить внешний вид маркера.
marker.setIcon("default.png");
marker.setIcon("active.png");
Пример:
marker.addListener("click", () => {
marker.setIcon(
"active.png"
);
});
Подобный приём часто заменяет полноценную анимацию.
Для современных карт рекомендуется использовать
AdvancedMarkerElement.
Он позволяет вставлять HTML-разметку внутрь маркера.
const markerContent =
document.createElement("div");
markerContent.className =
"custom-marker";
const marker =
new google.maps.marker.AdvancedMarkerElement({
map,
position: {
lat: 55.7558,
lng: 37.6176
},
content: markerContent
});
.custom-marker {
width: 20px;
height: 20px;
border-radius: 50%;
background: red;
animation:
pulse 1.5s infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
Получается эффект мигающей точки, характерный для геолокации и систем мониторинга.
.custom-marker {
animation:
rotateMarker 2s linear infinite;
}
@keyframes rotateMarker {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Подходит для индикаторов загрузки и специальных обозначений.
Можно объединять несколько CSS-анимаций.
.custom-marker {
animation:
pulse 2s infinite,
rotateMarker 4s linear infinite;
}
Одновременно выполняются увеличение и вращение.
Более гибкий подход — переключение CSS-классов.
.marker-active {
animation:
pulse 1s infinite;
}
Добавление:
markerContent.classList.add(
"marker-active"
);
Удаление:
markerContent.classList.remove(
"marker-active"
);
Такой способ особенно удобен для сложных интерфейсов.
При работе с большим количеством объектов необходимо учитывать ограничения браузера.
Основные рекомендации:
requestAnimationFrame для движения
объектов;Нагрузка особенно заметна при одновременной анимации нескольких сотен объектов, поэтому визуальные эффекты должны применяться избирательно и только там, где они действительно несут информационную ценность.