Element.animate() с использованием ключевых кадров, похоже, неправильно применяет функцию плавности

Я пытаюсь создать CSS-анимацию, которая прыгает вперед и назад с паузой на каждом конце. Использование Javascript-метода element.animate(), похоже, помогает, за исключением того, что использование ключевых кадров нарушает функцию плавности, чего не происходит при использовании только CSS.

Я неправильно использую animate()? Это ошибка? Почему Javascript и CSS ведут себя по-разному? В документации animate MDN об этом ничего не сказано.

const element = document.getElementById("animation")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: .1, transform: "translateX(0)" },
    { offset: .9, transform: "translateX(50vw)" },
    { offset: 1, transform: "translateX(50vw)" },
  ],
  {
    duration: 5000,
    easing: "ease-in-out",
    direction: "alternate",
    iterations: Infinity,
  }
);
<div id = "animation">Ball</div>

Создание такой же анимации в CSS работает при правильном замедлении.

const element = document.getElementById("animationJS")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: .1, transform: "translateX(0)" },
    { offset: .9, transform: "translateX(50vw)" },
    { offset: 1, transform: "translateX(50vw)" },
  ],
  {
    duration: 5000,
    easing: "ease-in-out",
    direction: "alternate",
    iterations: Infinity,
  }
);
#animationCSS {
    animation-name: move;
    animation-direction: alternate;
    animation-duration: 5s;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
}

@keyframes move {
    from {
      transform: translateX(0);
    }
    10% {
        transform: translateX(0);
    }
    90% {
        transform: translateX(50vw);
    }
    to {
        transform: translateX(50vw);
    }
}
<div id = "animationJS">Ball1</div>
<div id = "animationCSS">Ball2</div>

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

Кроме того, удаление двух средних ключевых кадров в JS-коде исправляет ситуацию, но это все равно не то, что мне нужно.

Хорошо, я превратил это в фрагмент. Вы уверены, что вас не смущает тот факт, что эта задержка встроена в вашу анимацию? Потому что похоже, что он делает именно то, что и ожидалось, плавно входя и выходя, но с вашими нынешними ценностями это может и не обязательно выглядеть так. Чтобы быть на 100% уверенным: увеличьте анимацию (сделайте ее 100vw или 1000px) и удалите все сбивающие с толку аспекты, чтобы увидеть, делает ли замедление то, что вы ожидаете (в этом случае удалите ключевые кадры .1 и .9, чтобы убедиться в правильности замедления). считает все время). Но в настоящее время: невозможно воспроизвести.

somethinghere 20.06.2024 14:01

@somethinghere Хорошо, я понимаю, что ты имеешь в виду. Кажется, оно действительно ускоряется. Однако поведение заметно отличается от поведения CSS: оно запускается и останавливается с гораздо большей скоростью при тех же числах. Вот почему я подумал, что облегчение выхода не работает. Я до сих пор озадачен несоответствием. Вот код для воспроизведения, просто удалите JS-файл и посмотрите, как значительно изменится анимация.

Fernandossmm 20.06.2024 14:19

Просто превратил ваш CSS во фрагмент, и нет, они выглядят совершенно одинаково. И они должны! Я добавил объединенную версию в свой ответ ниже, и анимация замедления выглядит почти так же, поэтому здесь мешает что-то еще. Ослабление работает так, как ожидалось (если это не так и никто, кроме вас, не заметил? Это маловероятно...)

somethinghere 20.06.2024 14:30

@somethinghere Я добавил код JS во фрагмент CSS. Разница в анимации между ними очевидна, без различий в параметрах. Кажется, что чистый CSS применяет функцию замедления к пошаговой анимации, в то время как код JS применяет функцию замедления ко всей анимации, как вы упомянули. Мне кажется, что в реализации JS есть ошибка или недокументированное поведение. О, вы объединили их в своем ответе. Дело в том, что я хочу отсрочки... Так что странность остается.

Fernandossmm 20.06.2024 15:09

Могу ли я дать еще одну подсказку: снимите пометку с моего ответа как «ответ», чтобы ваш вопрос не был завершен и вы могли бы привлечь людей, которые знают то, чего не знаем мы? (Я имею в виду, возможно, проголосуем за это в ответ, но в любом случае это нормально — просто хочу, чтобы вы нашли решение!)

somethinghere 20.06.2024 15:36

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

Fernandossmm 20.06.2024 15:53

Все хорошо. Это не прописанное правило, но когда ответ помечается как правильный, даже я начинаю игнорировать вопросы, так как не обязательно что-то добавлять. Удачи!

somethinghere 20.06.2024 23:33
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
7
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы удалите какие-либо сбивающие с толку аспекты анимации (поскольку замедление охватывает весь диапазон значений, поэтому оно начинается с 0 и заканчивается на 1, это означает, что вы пропускаете первые 10% замедления и последние 10 % части замедления) и немного увеличьте значения, чтобы действительно увидеть замедление в действии, вы заметите, что в настоящее время оно работает так, как ожидалось. В анимации легко запутаться, особенно если она тонкая.

const element = document.getElementById("animation")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: 1, transform: "translateX(100vw) translateX(-100%)" },
  ],
  {
    duration: 5000,
    easing: "ease-in-out",
    direction: "alternate",
    iterations: Infinity,
  }
);
#animation { position: absolute; left: 0; top: 0; }
<div id = "animation">Ball</div>

Вы можете увидеть это в приведенном выше фрагменте: ускориться, замедлиться: ease-in-out работает как положено. Я понимаю, чего вы пытаетесь достичь, имея 20%-ный перерыв между временными шкалами анимации, используя одну временную шкалу, но, насколько я понимаю, замедление применяется ко всей вашей временной шкале. Поэтому из-за этого вам может потребоваться настроить бесконечный цикл, чтобы добавить задержку:

async function delay( ms ){
    
  return new Promise(r => setTimeout(r, ms));
  
}
async function loopAnimation(){
  
  const element = document.getElementById("animation")

  let direction = 'normal';
  let animation;

  while( true ){
    
    await delay(1000);
    
    // Make sure previous an animations are removed and don't stack up
    if ( animation ) animation.cancel();
    
    animation = element.animate([
      { offset: 0, transform: "translateX(0)" },
      { offset: 1, transform: "translateX(100vw) translateX(-100%)" },
    ],{
      duration: 3000,
      easing: "ease-in-out",
      direction: direction,
      iterations: 1,
      fill: 'both'
    })
  
    await animation.finished;
    await delay(1000);
    
    // Reverse the animation
    direction = direction === 'normal' ? 'reverse' : 'normal';

  }

}

loopAnimation();
#animation { position: absolute; left: 0; top: 0; }
<div id = "animation">Ball</div>

Давайте объединим ваши примеры

Как вы можете видеть ниже, анимации на 99% одинаковы, я полагаю, что разница заключается лишь в небольшой разнице во времени с JS и загрузке, но они работают почти одинаково (мне пришлось исправить вашу анимацию в JS, чтобы она была .9, а не .8 в качестве смещения):

const element = document.getElementById("animate-js")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: .1, transform: "translateX(0)" },
    { offset: .9, transform: "translateX(10vw)" },
    { offset: 1, transform: "translateX(10vw)" },
  ],
  {
    duration: 5000,
    easing: "ease-in-out",
    direction: "alternate",
    iterations: Infinity,
  }
);
#animate-css {
  animation-name: move;
  animation-direction: alternate;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  color: red;
}

@keyframes move {
  from {
    transform: translateX(0);
  }
  10% {
    transform: translateX(0);
  }
  90% {
    transform: translateX(10vw);
  }
  to {
    transform: translateX(10vw);
  }
}
<div id = "animate-js">Animate</div>
<div id = "animate-css">Animate</div>

На самом деле нет, странность все равно в том, что вы пытаетесь добавить задержку в цикл:

const element = document.getElementById("animate-js")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: 1, transform: "translateX(10vw)" },
  ],
  {
    duration: 5000,
    easing: "ease-in-out",
    direction: "alternate",
    iterations: Infinity,
  }
);
#animate-css {
  animation-name: move;
  animation-direction: alternate;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  color: red;
}

@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(10vw);
  }
}
<div id = "animate-js">Animate</div>
<div id = "animate-css">Animate</div>

Без этого они идеально синхронизированы.

Это работает, большое спасибо! Хотя это гораздо сложнее, чем должно быть. Тот факт, что замедление применяется шаг за шагом с использованием CSS и в целом анимация с использованием JS, кажется большим упущением. Я обновил свой первоначальный вопрос фрагментами, которые четко иллюстрируют разницу между ними, с тем же набором параметров.

Fernandossmm 20.06.2024 14:33

Примечание: вам не обязательно отмечать это как правильный ответ, однако любопытно, что вы упомянули об этой разнице. Мне интересно, имеет ли это какое-либо отношение к таким вещам, как составная система в JS, которую CSS не поддерживает?

somethinghere 20.06.2024 14:36
Ответ принят как подходящий

Я нашел решение — соответствующий JS-код, который работает так же, как и CSS.

const element = document.getElementById("animationJS")
const anim = element.animate(
  [
    { offset: 0, transform: "translateX(0)" },
    { offset: .1, transform: "translateX(0)", easing: "ease-in-out" },
    { offset: .9, transform: "translateX(50vw)"},
    { offset: 1, transform: "translateX(50vw)" },
  ],
  {
    duration: 5000,
    direction: "alternate",
    iterations: Infinity,
  }
);
#animationCSS {
    animation-name: move;
    animation-direction: alternate;
    animation-duration: 5s;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
}

@keyframes move {
    from {
      transform: translateX(0);
    }
    10% {
        transform: translateX(0);
    }
    90% {
        transform: translateX(50vw);
    }
    to {
        transform: translateX(50vw);
    }
}
<div id = "animationJS">Ball1</div>
<div id = "animationCSS">Ball2</div>

Добавление функции замедления только к средней части анимации и удаление ее из всей анимации дает результат.

Я понятия не имею, почему это так работает, и не могу найти этой конкретной разницы в документации, но, по крайней мере, это работает.

Другие вопросы по теме