Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про JavaScript: animation, performance

Когда используют requestAnimationFrame?

Этот вопрос проверяет понимание оптимизации анимаций и визуальных обновлений в браузере, а также знание отличий requestAnimationFrame от других методов тайминга.

Короткий ответ

requestAnimationFrame используют для плавной анимации и визуальных обновлений, синхронизированных с частотой обновления экрана. Он обеспечивает оптимальную производительность, автоматически приостанавливая выполнение когда страница не видна, и вызывая колбэк перед перерисовкой браузера.

Длинный ответ

Определение requestAnimationFrame

requestAnimationFrame - это браузерный API, который позволяет выполнять код синхронно с обновлением экрана, обычно 60 раз в секунду.

Основные случаи использования

Создание плавной анимации

javascript

function animateElement(element) {
    let startTime = null;
    const duration = 2000; // 2 секунды
    
    function step(timestamp) {
        if (!startTime) startTime = timestamp;
        const progress = timestamp - startTime;
        
        // Вычисление текущей позиции
        const translateX = (progress / duration) * 200;
        element.style.transform = `translateX(${translateX}px)`;
        
        // Продолжение анимации
        if (progress < duration) {
            requestAnimationFrame(step);
        }
    }
    
    requestAnimationFrame(step);
}

Оптимизация частых визуальных обновлений

javascript

class SmoothScroller {
    constructor() {
        this.isScrolling = false;
        this.targetScroll = 0;
    }
    
    scrollTo(target) {
        this.targetScroll = target;
        if (!this.isScrolling) {
            this.animateScroll();
        }
    }
    
    animateScroll() {
        this.isScrolling = true;
        const currentScroll = window.pageYOffset;
        const distance = this.targetScroll - currentScroll;
        
        if (Math.abs(distance) < 1) {
            this.isScrolling = false;
            return;
        }
        
        // Плавное замедление
        const newScroll = currentScroll + distance * 0.1;
        window.scrollTo(0, newScroll);
        
        requestAnimationFrame(() => this.animateScroll());
    }
}

Преимущества перед setTimeout/setInterval

Синхронизация с браузером

  • Автоматическая пауза когда страница неактивна или скрыта

  • Согласование с частотой обновления экрана (обычно 60fps)

  • Выполнение перед рендерингом - гарантия актуальности данных

Сравнение подходов

javascript

// Плохо - с setTimeout
function animateWithTimeout() {
    element.style.left = (parseInt(element.style.left) + 1) + 'px';
    setTimeout(animateWithTimeout, 16); // ~60fps
}

// Хорошо - с requestAnimationFrame
function animateWithRAF() {
    element.style.left = (parseInt(element.style.left) + 1) + 'px';
    requestAnimationFrame(animateWithRAF);
}

Практические примеры применения

Игровая графика и анимации

javascript

class GameEngine {
    constructor() {
        this.lastTime = 0;
        this.isRunning = false;
    }
    
    start() {
        this.isRunning = true;
        this.gameLoop(0);
    }
    
    gameLoop(timestamp) {
        if (!this.isRunning) return;
        
        // Вычисление delta time для независимости от FPS
        const deltaTime = timestamp - this.lastTime;
        this.lastTime = timestamp;
        
        // Обновление игрового состояния
        this.update(deltaTime);
        
        // Отрисовка кадра
        this.render();
        
        // Следующий кадр
        requestAnimationFrame((time) => this.gameLoop(time));
    }
    
    update(deltaTime) {
        // Обновление позиций объектов, физика и т.д.
    }
    
    render() {
        // Отрисовка игровых объектов
    }
    
    stop() {
        this.isRunning = false;
    }
}

Измерение производительности

javascript

function measureFPS() {
    let frameCount = 0;
    let lastTime = performance.now();
    let fps = 0;
    
    function countFrame() {
        frameCount++;
        const currentTime = performance.now();
        
        if (currentTime - lastTime >= 1000) {
            fps = frameCount;
            frameCount = 0;
            lastTime = currentTime;
            console.log(`FPS: ${fps}`);
        }
        
        requestAnimationFrame(countFrame);
    }
    
    requestAnimationFrame(countFrame);
}

Особенности работы

Время выполнения колбэка

javascript

requestAnimationFrame((timestamp) => {
    // timestamp - время с момента загрузки страницы в миллисекундах
    // Точное время когда будет выполнен рендеринг
    console.log(`Кадр будет отрисован в: ${timestamp}`);
});

Отмена анимации

javascript

let animationId = null;

function startAnimation() {
    function animate() {
        // Логика анимации
        animationId = requestAnimationFrame(animate);
    }
    animationId = requestAnimationFrame(animate);
}

function stopAnimation() {
    if (animationId) {
        cancelAnimationFrame(animationId);
        animationId = null;
    }
}

Best Practices

Оптимизация производительности

javascript

function efficientAnimation() {
    let rafId = null;
    
    function animationStep() {
        // Выполняем только необходимые вычисления
        performCalculations();
        
        // Проверяем нужно ли продолжать
        if (shouldContinueAnimation()) {
            rafId = requestAnimationFrame(animationStep);
        } else {
            rafId = null;
        }
    }
    
    function start() {
        if (!rafId) {
            rafId = requestAnimationFrame(animationStep);
        }
    }
    
    function stop() {
        if (rafId) {
            cancelAnimationFrame(rafId);
            rafId = null;
        }
    }
    
    return { start, stop };
}

Батчинг обновлений

javascript

class BatchedAnimator {
    constructor() {
        this.callbacks = new Set();
        this.rafId = null;
    }
    
    addCallback(callback) {
        this.callbacks.add(callback);
        this.scheduleFrame();
    }
    
    removeCallback(callback) {
        this.callbacks.delete(callback);
        if (this.callbacks.size === 0 && this.rafId) {
            cancelAnimationFrame(this.rafId);
            this.rafId = null;
        }
    }
    
    scheduleFrame() {
        if (!this.rafId) {
            this.rafId = requestAnimationFrame(() => {
                this.rafId = null;
                this.executeCallbacks();
            });
        }
    }
    
    executeCallbacks() {
        this.callbacks.forEach(callback => {
            try {
                callback();
            } catch (error) {
                console.error('Animation error:', error);
            }
        });
    }
}

Вывод: requestAnimationFrame следует использовать для любых визуальных обновлений и анимаций, где важна плавность и производительность. Он обеспечивает оптимальную синхронизацию с браузерным рендерингом и автоматически адаптируется к системным ресурсам.

Уровень

  • Рейтинг:

    4

  • Сложность:

    6

Навыки

  • JavaScript

    JavaScript

Ключевые слова

#animation

#performance

Подпишись на React Developer в телеграм