Как определить направление прокрутки при изменении высоты содержимого при прокрутке

#javascript #css #reactjs #user-interface #animation

#javascript #css #reactjs #пользовательский интерфейс #Анимация

Вопрос:

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

Пример песочницы, где, если вы прокрутите до точки, где она начинает уменьшаться, она начинает прыгать.

Одним из возможных решений, которое я придумал, было добавление разделителя, который компенсировал бы, когда липкая навигация становится меньше. Но для этого требуется сделать это в самом компоненте. Интересно, есть ли способ сделать это с помощью js только в useScrollDirection хук.

Одна из других вещей, которые я пробовал, — это использовать document.documentElement.scrollHeight и проверять изменение размера, но это тоже не помогло.

Ответ №1:

Просто предложение. Я думаю, вы position: sticky недостаточно используете. Вам не нужен прослушиватель событий для достижения желаемого поведения. Вы можете сделать это полностью с помощью CSS и позволить браузеру обрабатывать все за вас.

 .app {
  font-family: sans-serif;
  text-align: center;
  height: 300vh;
}

.nav {
  position: sticky;
  top: -120px;
  height: 150px;
  background: aquamarine
}

.nav_item {
   position: sticky;
    top: 0px;
    margin: 0;
} 
 <div class="app">
  <h1>Hello CodeSandbox</h1>
  <nav class="nav">
    <p class="nav_item">Hello</p>
  </nav>
  <h2>Start editing to see some magic happen!</h2>
</div> 

Комментарии:

1. Спасибо @slumbergeist! Я мог бы попробовать использовать это! но этот пример довольно упрощен до сути из реального приложения. В реальном приложении есть изменение высоты шрифта и несколько других вещей, о которых нужно сигнализировать с помощью прокрутки.

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

3. Я обновил пример с изменением шрифта.

Ответ №2:

Я столкнулся с той же проблемой, и коллега порекомендовал мне прослушать wheel событие. Итак, я реализовал следующий реагирующий хук:

 const useLastMouseWheelDirection = () => {
  const [lastMouseWheelDirection, setLastMouseWheelDirection] = useState(
    undefined
  )

  useEffect(() => {
    const handler = (event) => {
      setLastMouseWheelDirection(event.deltaY >= 0 ? 'down' : 'up')
    }
    window.addEventListener('wheel', handler)
    return () => window.removeEventListener('wheel', handler)
  }, [])

  return lastMouseWheelDirection
}
 

Это решение имеет некоторые серьезные ограничения:

  • Очевидно, что события касания игнорируются
  • Нажатие и перетаскивание полосы прокрутки не вызовет событие

Также побочным эффектом желаемого поведения было бы то, что он не будет учитывать, переполнен элемент или нет, хотя перехват может быть расширен, чтобы уловить этот случай.

Далее в MDN Web Docs указано:

Примечание: Не путайте событие колеса с событием прокрутки. Действие по умолчанию для события колеса зависит от реализации и не обязательно отправляет событие прокрутки. Даже когда это происходит, значения delta * в событии wheel не обязательно отражают направление прокрутки содержимого. Поэтому не полагайтесь на свойства delta * события wheel для определения направления прокрутки. Вместо этого обнаружьте изменения значений scrollLeft и scrollTop целевого объекта в событии прокрутки.

Поэтому, пожалуйста, отнеситесь к этому ответу со всей серьезностью.