x
 
Александр Исайчев
26 марта 2020
Советы почтой каждую неделю
Пожалуйста, получите наше письмо, чтобы подтвердить свой адрес:
Вы подписаны на «Советы за неделю»:

Добрый день!

Какую библиотеку JS вы используете при смещении фона в проекте «Конструктор плакатов и презентаций Актиона» в блоке «Плакат окулиста» в нижней части страницы?


Александр!

Какой‑то гото­вой биб­лио­теки здесь нет. Дизай­неры сами собрали эту ани­ма­цию, исполь­зуя сред­ства, встро­ен­ные в дви­жок сайта:

Под капо­том — стан­дарт­ный бра­у­зер­ный АПИ веб‑ани­ма­ций и немного кода для при­вязки его к скроллу

Покажу на при­мере, как сде­лать похо­жее. Возь­мём несколько дивов с фоном и запол­ним ими 1,5 экрана:

<style>
.prospect {
  /*
    Фик­си­руем высоту кон­тей­нера, чтобы с помо­щью flex-grow
    рас­пре­де­лить в нём место
  */

  --prospectHeight: 150vh;
  /*
     Фик­си­руем ширину рядов. Она должна быть больше
     ширины экрана, чтобы было, что про­кру­чи­вать
  */
  --prospectWidth: 150vw;
  
  /* Задаем мак­си­маль­ный меж­строч­ник и меж­ко­лон­ник */
  --prospectGutter: 30px;
 
  height: var(--prospectHeight);
  display: flex;
  flex-flow: column;
  overflow: hidden;
}
​
.prospect > div {
  width: var(--prospectWidth);
  /*
     Мас­шта­би­руем ряды в соот­вет­ствии с коэф­фи­ци­ен­том,
     задан­ным им вруч­ную через ЦСС-пере­мен­ные
  */
  flex-grow: var(--factor);
}
​
.prospect div:not(:last-child) {
  /* Ана­ло­гично мас­шта­би­руем меж­строч­ник */
  margin-bottom: calc(var(--prospectGutter) * var(--factor));
}
</style>
​ 
<div class="propspect">
  <div style="--factor: .5; --speed: 1"></div>
  <div style="--factor: .45; --speed: 1.25"></div>
  <div style="--factor: .35; --speed: 1"></div>
  <div style="--factor: .3; --speed: 1.15"></div>
  <div style="--factor: .25; --speed: 1"></div>
  <div style="--factor: .175; --speed: .9"></div>
  <div style="--factor: .1; --speed: 1"></div>
  <div style="--factor: .05; --speed: .8"></div>
  <div style="--factor: .045; --speed: .75"></div>
  <div style="--factor: .035; --speed: .7"></div>
  <div style="--factor: .025; --speed: .65"></div>
  <div style="--factor: .02; --speed: .6"></div>
</div>

Пусть нечёт­ные ряды дви­га­ются справа налево, а чёт­ные — слева направо. Ани­ми­руем их:

// Опи­сы­ваем ани­ма­ции для чёт­ных и нечёт­ных рядов
const oddAnimation = [
  { transform: 'translateX(0)', offset: 0 },
  { transform: 'translateX(-50vw)', offset: 1 },
]
​
const evenAnimation = [
  { transform: 'translateX(-50vw)', offset: 0 },
  { transform: 'translateX(0)', offset: 1 },
]
​
const prospect = document.querySelector('.prospect')
const prospectRows = document.querySelectorAll('.prospect > div')
​
// Опре­де­ляем коор­ди­наты кон­тей­нера и задаём гра­ницы ани­ма­ции:
// ани­ма­ция нач­нётся с верх­ней гра­ницы блока и закон­чится на ниж­ней
const initialScrollTop = window.pageYOffset
const prospectRect = prospect.getBoundingClientRect()
const animateFrom = initialScrollTop + prospectRect.top
const animateTo = initialScrollTop + prospectRect.bottom
​
// Соби­раем мас­сив ани­ми­ро­ван­ных рядов
const animations = Array.from(prospectRows).map((row, index) => {
  const animation = (index + 1) % 2 === 0 ? evenAnimation : oddAnimation
  const speedFactor = row.style.getPropertyValue('--speed')
  ​
  // Ини­ци­а­ли­зи­руем ани­ма­цию ряда и тут же ста­вим её на паузу:
  // мы сами будем пере­ма­ты­вать ани­ма­цию в зави­си­мо­сти от пози­ции скролла
  const player = row.animate(animation, { duration: 1 })
  player.pause()

  return { player, speedFactor }
})
​
​
const animate = () => {
  const scrollTop = window.pageYOffset
  // Опре­де­ляем, какой про­цент ани­ма­ции уже про­скрол­лили
  const scrollPosition = (scrollTop - animateFrom) / (animateTo - animateFrom)
  ​
  animations.forEach((animation) => {
    let animationPosition = scrollPosition * animation.speedFactor
    // Обра­ба­ты­ваем погра­нич­ные слу­чаи: не даём ани­ма­ции закон­читься
    if (animationPosition < 0) animationPosition = 0
    if (animationPosition >= 1) animationPosition = 0.999
    ​
    // Пере­ма­ты­ваем ани­ма­цию на нуж­ный нам кадр
    animation.player.currentTime = animationPosition
  })
  ​
  // В бое­вых про­ек­тах стоит затро­т­лить этот вызов
  requestAnimationFrame(animate)
}
​
animate()
P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.
Вёрстка и прототипирование — дисциплина Школы дизайнеров. Набор открыт. Чем раньше поступите, тем ниже стоимость и выше шанс на бесплатное место.
 

Поделиться
Отправить

Цель рубрики — обсуждение вопросов дизайна всех видов, текста в дизайне и взаимоотношений дизайнеров с клиентами.

Мы публикуем комментарии, которые добавляют к уже сказанному новые мысли и хорошие примеры. Мы ожидаем, что такие комментарии составят около 20% от общего числа.

Решение о публикации принимается один раз; мы не имеем возможности комментировать или пересматривать свое решение, хотя оно может быть ошибочно. Уже опубликованные комментарии могут быть удалены через некоторое время, если без них обсуждение не становится менее ценным или интересным.

Вот такой веб 2.0.

Как быть, если всё моё время уходит на разработку всё новых и новых фич? Типовые решения в вёрстке. Как сверстать простую шапку страницы с меню 4 Как написать аккуратный код? Часть вторая: связность 1 Адаптивные изображения, тег picture




Недавно всплыло

Сервис для перевода денег между картами. Часть 2 3 5 Как редактору вести себя в соцсетях? 3 2