← Назад к вопросам

Можно ли сделать анимацию, используя только промисы?

1.0 Junior🔥 221 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Анимация с использованием Promise

Да, можно создавать анимации, используя только промисы, но это не лучший подход. Давайте разберём, как это работает, какие есть альтернативы и почему они лучше.

Можно ли: Пример с setTimeout + Promise

// Функция для анимации элемента
function animateElement(element, duration = 1000) {
  return new Promise((resolve) => {
    const startTime = Date.now();
    const startOpacity = parseFloat(
      window.getComputedStyle(element).opacity
    );

    function animate() {
      const elapsed = Date.now() - startTime;
      const progress = Math.min(elapsed / duration, 1);
      
      element.style.opacity = startOpacity + (1 - startOpacity) * progress;
      
      if (progress < 1) {
        setTimeout(animate, 16); // ~60 FPS
      } else {
        resolve();
      }
    }

    animate();
  });
}

// Использование
const box = document.getElementById('box');
await animateElement(box, 1000);
console.log('Анимация завершена!');

Цепочка анимаций через Promise.then()

const element = document.getElementById('box');

// Анимация 1: Фейд ин
function fadeIn(el, duration = 500) {
  return new Promise((resolve) => {
    el.style.transition = `opacity ${duration}ms`;
    el.style.opacity = 1;
    setTimeout(resolve, duration);
  });
}

// Анимация 2: Движение вправо
function slideRight(el, distance = 100, duration = 500) {
  return new Promise((resolve) => {
    el.style.transition = `transform ${duration}ms`;
    el.style.transform = `translateX(${distance}px)`;
    setTimeout(resolve, duration);
  });
}

// Анимация 3: Фейд аут
function fadeOut(el, duration = 500) {
  return new Promise((resolve) => {
    el.style.transition = `opacity ${duration}ms`;
    el.style.opacity = 0;
    setTimeout(resolve, duration);
  });
}

// Последовательное выполнение
fadeIn(element, 500)
  .then(() => slideRight(element, 100, 500))
  .then(() => fadeOut(element, 500))
  .then(() => {
    console.log('Все анимации завершены!');
    element.style.display = 'none';
  })
  .catch(err => console.error('Ошибка:', err));

Или с async/await (более читаемо)

async function runSequentialAnimations() {
  const box = document.getElementById('box');
  
  try {
    await fadeIn(box, 500);
    await slideRight(box, 100, 500);
    await fadeOut(box, 500);
    console.log('Готово!');
  } catch (error) {
    console.error('Ошибка анимации:', error);
  }
}

runSequentialAnimations();

Почему это НЕ лучший способ

1. Проблема с производительностью

// Плохо: requestAnimationFrame не используется
function slowAnimate(el, duration) {
  return new Promise((resolve) => {
    const start = Date.now();
    const interval = setInterval(() => {
      const elapsed = Date.now() - start;
      const progress = elapsed / duration;
      el.style.left = (progress * 100) + 'px';
      
      if (progress >= 1) {
        clearInterval(interval);
        resolve();
      }
    }, 16); // Может быть пропущено браузером
  });
}

2. Отсутствие оптимизации браузера

RequestAnimationFrame синхронизируется с fps браузера и пропускает кадры, если вкладка невидима.

3. Сложность управления

Остановить, пауза, перезагрузка — сложнее реализовать с Promise.

Лучший способ: requestAnimationFrame + Promise

function animateWithRAF(element, keyframes, duration) {
  return new Promise((resolve) => {
    const startTime = performance.now();

    function frame(currentTime) {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);

      // Применяем keyframes
      const currentFrame = keyframes(progress);
      Object.assign(element.style, currentFrame);

      if (progress < 1) {
        requestAnimationFrame(frame);
      } else {
        resolve();
      }
    }

    requestAnimationFrame(frame);
  });
}

// Использование
await animateWithRAF(box, (progress) => ({
  opacity: progress,
  transform: `translateX(${progress * 100}px)`
}), 1000);

console.log('Готово!');

Лучший способ: CSS Transitions + Promise

function animateWithCSS(element, className, duration) {
  return new Promise((resolve) => {
    const handleTransitionEnd = () => {
      element.removeEventListener('transitionend', handleTransitionEnd);
      resolve();
    };

    element.addEventListener('transitionend', handleTransitionEnd, { once: true });
    element.classList.add(className);
  });
}

// CSS
// .fade-in {
//   transition: opacity 0.5s ease;
//   opacity: 1;
// }

// Использование
await animateWithCSS(box, 'fade-in', 500);
console.log('Анимация завершена!');

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

ПодходПроизвод-ностьПростотаУправлениеИспользование
Promise + setTimeoutСредняяСредняяСложноНе рекомендуется
Promise + requestAnimationFrameХорошаяСредняяСреднеХорошо
CSS Transitions + PromiseОтличнаяПростаяСреднеРекомендуется
Framer Motion / Anime.jsОтличнаяПростаяЛегкоЛучше всего

Вывод

Да, можно сделать анимацию с Promise, но это не оптимально. Используйте:

  1. CSS Transitions/Animations — для простых анимаций
  2. requestAnimationFrame — для сложных анимаций на JavaScript
  3. Библиотеки (Framer Motion, Anime.js) — для комплексных эффектов
  4. Promise — только для управления последовательностью, не для самой анимации

Combining Promise с правильными методами анимации (RAF или CSS) даёт лучший результат.

Можно ли сделать анимацию, используя только промисы? | PrepBro