Context :
Встроенный слой тепловой карты в API Карт Google не имеет встроенной поддержки анимации, поэтому я создал собственные анимации, используя requestAnimationFrame
с вызовами API Карт для установки данных следующего кадра. Каждый кадр использует ~ 2500 точек данных для рендеринга тепловой карты и максимально достигает 4000 точек данных.
Проблема :
Моя анимация имеет рывки каждые 5-10 циклов. Мне не нужна анимация для рендеринга 60 кадров в секунду, но я хочу, чтобы частота кадров была постоянной, а каждый шаг был <= 100 мс. В Devtools кадры анимации дают мне эту ошибку в верхней части стека вызовов: «Предупреждение: повторяющийся обработчик занял # мс.» </p>
Моя предполагаемая причина проблемы :
Консоль разработчика показывает утечки памяти, * второстепенные GC, основные GC и DOM GC, связанные с вызовами Maps API. В частности, DOM GC соотносится с анимацией. (* JS Heap, Nodes и Listeners увеличиваются линейно).
Почему я задаю вопрос :
Я довольно плохо знаком с API Карт Google, requestAnimationFrame и управлением памятью в JavaScript, поэтому прежде всего я хочу убедиться, что правильно понимаю проблему. Во-вторых, я хочу посмотреть, есть ли способ оптимизировать слой тепловой карты для анимации или мой подход ошибочен.
Наиболее важные ресурсы, с которыми я консультировался :
Документация по API Карт Google
Руководство Google по производительности в сети
Вопрос StackOverflow, в котором задаются вопросы об очень простых и коротких анимациях тепловой карты API Карт
Код
Все в пределах d3.csv ('data').
Установить рекурсивный запросAnimationFrame:
const periodDuration = 100; // ms
const timeStart = performance.now();
const frameTick = timestamp => {
if (timestamp - timeStart >= periodDuration) {
timeStart = timestamp;
transitionToNextPeriod(); // shown in next code snippet
}
requestAnimationFrame(frameTick);
};
requestAnimationFrame(frameTick);
transitionToNextPeriod
увеличивает время, затем фильтрует данные и завершает работу, устанавливая тепловую карту с обновленными данными:
function transitionToNextPeriod() {
let batchedData = [],
currPeriodData = [],
addMin = 0;
prevMin = min,
prevDate = date,
currMin = 0;
// batch the next 'x' # of mins for one batchedData call after `while` loop
while (++addMin <= 7){
if (prevMin + addMin === 60){
if (hour + 1 === 24){
hour = 0;
date++;
} else {
hour++
}
}
// Now with current minute, filter data and add to batchedData
currMin = ((prevMin + addMin) % 60);
let newTime = `${hour}:${zeroPad(currMin)}`;
// Filter based on current time
let currFilter = dayData.filter( d => d.StartTime.indexOf( ` ${newTime}` ) !== -1);
prevPeriodData = prevPeriodData.filter( d => d.EndTime.indexOf( ` ${newTime}` ) === -1 );
batchedData = [...batchedData, ...currFilter ];
} // End while
batchedData = [...batchedData, ...prevPeriodData];
prevPeriodData = batchedData;
min = currMin;
// Update the maps.LatLng data with new data, then set heatmap data
batchedData.forEach( (d,i) => {
// Check for last datum in current period
if (i + 1 === batchedData.length){
heatmap.setData(currPeriodData); // PROBLEM: about every 7th call to setData results in jank
} else {
currPeriodData.push(new google.maps.LatLng( d.Lat , d.Long ));
}
})
} // End transitionToNextPeriod()