Сделайте элемент липким до тех пор, пока анимация, управляемая прокруткой, не завершится

Вот чего я пытаюсь достичь:

-когда элементы становятся видимыми пользователю, для них один за другим запускается анимация вращения при прокрутке, и все 3 элемента становятся липкими (это означает, что они не исчезают, пока пользователь прокручивает вниз), пока анимация на последний закончен. -когда анимация завершена, страница возвращается к своему обычному поведению

Исполняемый код:

Этот код не делает элементы липкими, когда это необходимо.

document.addEventListener('DOMContentLoaded', function() {
  const elements = document.querySelectorAll('.animation');
  let delay = 0;

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.style.animationDelay = `${delay}s`;
        entry.target.style.animationPlayState = 'running';
        console.info('running');

        // Add sticky class when the element is in view
        entry.target.classList.add('sticky');

        delay += 2.5; // Increase delay for the next element
        observer.unobserve(entry.target); // Stop observing once the animation is triggered
      }
    });
  }, {
    threshold: 0.5
  }); // Trigger when 50% of the element is in view

  elements.forEach(element => {
    console.info('paused');
    element.style.animationPlayState = 'paused'; // Pause animation until triggered
    observer.observe(element);

    // Remove sticky class after the animation ends
    element.addEventListener('animationend', () => {
      console.info('animationend')
      element.classList.remove('sticky');
    });
  });
});
.content {
  width: 75%;
  max-width: 800px;
  margin: 0 auto;
}

p,
h1 {
  font-family: Arial, Helvetica, sans-serif;
}

h1 {
  font-size: 3rem;
}

p {
  font-size: 1.5rem;
  line-height: 1.5;
}

.animation {
  view-timeline: --subjectReveal block;
  animation-timeline: --subjectReveal;
  width: 200px;
  height: 200px;
  background-color: bisque;
  animation-iteration-count: 1;
  animation-name: mymove;
  animation-duration: 3s;
  position: relative;
  /* Normal position */
}

.animation:nth-child(1) {
  animation-delay: calc(var(--subjectReveal) * 1);
}

.animation:nth-child(2) {
  animation-delay: calc(var(--subjectReveal) * 2);
}

.animation:nth-child(3) {
  animation-delay: calc(var(--subjectReveal) * 3);
}


/* Define the sticky behavior */

.sticky {
  position: sticky;
  top: 10px;
  /* Adjust top as needed */
  z-index: 10;
  /* Ensure it stays on top */
}

@keyframes mymove {
  0% {
    opacity: 1;
    transform: rotate(0deg);
  }
  50% {
    opacity: 1;
    transform: rotate(90deg);
  }
  100% {
    opacity: 1;
    transform: rotate(180deg);
  }
}




.as-console-wrapper { height:50px; }
Scroll this
<div style = "height:1000px; min-height: 300px; max-width: 100%; display: flex; flex-direction: row; justify-content: space-between; align-items: center;">
  <div class = "animation"></div>
  <div class = "animation"></div>
  <div class = "animation"></div>
</div>

Эта анимация возникает только при прокрутке, но в текущем коде, похоже, нет места для прокрутки. Должен ли я произвольно увеличить высоту и воспроизвести ее?

NINE 29.08.2024 10:18

@NINE Я добавил только html-часть с элементами, к которым применяется анимация.

kirvel 29.08.2024 10:31

Я немного изменил ваш CSS (<div style = "height:1000px;), чтобы показать эффект, и ограничил высоту консоли фрагмента.

mplungjan 29.08.2024 10:45

@mplungjan спасибо за разъяснение моего кода! есть идеи, как реализовать то, что показано на гифке?

kirvel 29.08.2024 11:09

Не могли бы вы прежде всего уточнить, как именно вы хотите, чтобы это вело себя? Ваше анимированное изображение предполагает, что анимация поворота начнется, когда элементы достигнут середины экрана, но в вашем коде написано // Trigger when 50% of the element is in view. А в вашем классе sticky есть top: 10px;, что означает, что элементы станут липкими только тогда, когда они находятся на расстоянии менее 10 пикселей от верха прокручиваемого контейнера - это также не соответствует тому, чего вы на самом деле хотите достичь.

C3roe 29.08.2024 11:18
// Remove sticky class after the animation ends — ваше изображение предполагает, что вы хотите, чтобы они все стали липкими, а затем снова отклеились одновременно. Но ваши анимации завершатся в разное время из-за разных начальных задержек — поэтому не следует удалять класс из каждого отдельного элемента, когда закончилась его собственная анимация; но удалите его из всех, когда анимация последнего элемента завершится.
C3roe 29.08.2024 11:21

Вы уже определили animation-delay в своем CSS — не знаете, зачем тогда вам нужно снова устанавливать его в части JS? Разве не имело бы гораздо больше смысла, если бы вы просто наблюдали пересечение одного из этих элементов (поскольку они все равно находятся в одной и той же позиции по оси Y), а затем запускали анимацию для всех из них (например, просто задав контейнеру элемент класса) - с задержками, уже установленными в CSS?

C3roe 29.08.2024 11:26
Поведение ключевого слова "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
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хотите попробовать это?

Я использовал CSS animation-range-start, он был в предоставленном вами коде..

но кажется, что поддерживать ее в Firefox и Safari сложно, поскольку это экспериментальная функция.

Я поработал над движениями, которые вам нужны, поэтому используйте те части, на которые вы можете обратиться!

Надеюсь, это поможет :)

document.addEventListener('DOMContentLoaded', function() {
  const elementsContent = document.querySelector('.animation-content');
  const elements = document.querySelectorAll('.animation');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        elements.forEach((element) => {
          element.style.animationName = 'mymove'; // add animation to div
          element.style.animationPlayState = 'running'; // Start animation
        });
        observer.unobserve(entry.target); // Stop observing
      }
    });
  }, {
    threshold: 0.8
  }); // Trigger when 80% of the element is in view

  observer.observe(elementsContent);
});
html,
body {
  margin: 0;
  padding: 2vw;
  background: #222;
}

.animation-wrap {
  height: 300vh;
  background: #fff;
}

.animation-content {
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 100vh;
  position: sticky;
  top: 0;
}

.animation {
  view-timeline: --subjectReveal block;
  animation-timeline: --subjectReveal;
  animation-range-start: entry 200%;
  height: 0; /* ignore this, just for square */
  width: 20vw; /* ignore this, just for square */
  padding-bottom:20vw; /* ignore this, just for square */
  background-color: bisque;
  animation-iteration-count: 1;
  animation-duration: 3s;
  animation-fill-mode: forwards;
  /*whenendanimation,holdstartstate*/
  animation-play-state: paused;
  /*pausedatfirst*/
  position: relative;
  /*Normalposition*/
}

.animation:nth-child(1) {
  animation-delay: 0s;
}

.animation:nth-child(2) {
  animation-delay: 1.4s;
}

.animation:nth-child(3) {
  animation-delay: 2.8s;
}

@keyframes mymove {
  0% {
    transform: rotate(0deg);
  }
  25% {
    transform: rotate(360deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

div[class* = "other-content"] {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 3rem;
}

.other-content_01 {
  height: 400px;
  background: #eee;
}

.other-content_02 {
  height: 1000px;
  background: #ddd;
}
<!DOCTYPE html>
<html lang = "en">

<head>
  <meta charset = "UTF-8">
  <meta name = "viewport" content = "width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel = "stylesheet" href = "test.css">
</head>

<body>
  <div class = "other-content_01">
    Scroll Page :)
  </div>
  <div class = "animation-wrap">
    <div class = "animation-content">
      <div class = "animation"></div>
      <div class = "animation"></div>
      <div class = "animation"></div>
    </div>
  </div>
  <div class = "other-content_02">
    Thank you!
  </div>

  <script src = "test.js"></script>
</body>

</html>

Спасибо большое, именно этого я и добивался :)

kirvel 29.08.2024 16:25

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