Эффект иммерсивного перехода на CSS и JavaScript

В этом уроке мы покажем вам, как создать эффект погружения между секциями.



ДЕМО
ИСХОДНИКИ


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

Во время прокрутки медиа-элемент должен быть зафиксирован, когда он достигнет центра области просмотра. С этого момента он должен масштабироваться до анимации в полноэкранном элементе. После завершения анимации блок содержимого должен начать прокручиваться по ней.


<div class="immerse-section-tr js-immerse-section-tr">
 <div class="immerse-section-tr__media js-immerse-section-tr__media">
 <div class="container max-width-sm">
 <figure class="immerse-section-tr__figure js-immerse-section-tr__figure">
 <!-- video/img element here -->
 </figure>
 </div>
 </div>
 <section class="immerse-section-tr__content bg-transparent js-immerse-section-tr__content">
 <!-- some generic content here -->
 </section>
</div>

Чтобы зафиксировать медиа-элемент во время прокрутки, мы можем использовать закрепленное значение свойства CSS position.

.immerse-section-tr__media {
  position: sticky;
}

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

Во-первых: анимация масштаба происходит во время прокрутки пользователя; это означает, что во время анимации часть элемента .immerse-section-tr__content (общий блок содержимого) уже была видна. Чтобы решить эту проблему, мы можем добавить к этому элементу верхний край, равный величине прокрутки, необходимой для анимации. Это гарантирует, что во время масштабной анимации не будет отображаться новый контент.

Во-вторых: поскольку мы увеличиваем масштаб ресурса, нам нужно применить скрытое переполнение, чтобы убедиться, что горизонтальная полоса прокрутки не видна во время анимации. К сожалению, липкая позиция не работает, если у одного из родителей скрыт перелив. По этой причине мы должны применить свойство переполнения непосредственно к элементу с фиксированной позицией (элемент .immerse-section-tr__media).

Однако для правильной работы переполнения этот элемент должен иметь высоту, равную высоте области просмотра. Используя JavaScript, мы устанавливаем его высоту, равную высоте области просмотра, и добавляем верхний отступ, равный половине разницы между областью просмотра и высотой актива (чтобы убедиться, что элемент ресурса по-прежнему фиксируется, когда он находится в центре области просмотра) . Чтобы сбалансировать дополнительное пространство, создаваемое отступом, мы также добавляем отрицательное верхнее поле, равное этому верхнему отступу. Выполнено!

Из-за ограничений, описанных выше, эффект отключается, если:

  • видео выше области просмотра; эффект масштабирования оставит часть актива недоступной для пользователей;
  • (высота области просмотра - высота видео) больше 600 пикселей. Это потребует огромного количества дополнительной прокрутки и пустого пространства под активом, что повлияет на удобство использования.

Для завершения анимации нам еще необходимо:

  1. Обнаруживать, когда медиа-элемент входит в область просмотра, чтобы вызвать масштабную анимацию, используя API-интерфейс Intersection Observer;
  2. Анимировать масштаб на основе значения прокрутки, используя событие прокрутки окна;
  3. Удалите прослушиватель событий прокрутки, как только элемент покинет область просмотра.

Мы можем создать объект ImmerseSectionTr для реализации эффекта иммерсивного перехода:


var ImmerseSectionTr = function(element) {
  this.element = element;
  this.media = this.element.getElementsByClassName('js-immerse-section-tr__media');
  this.scrollContent = this.element.getElementsByClassName('js-immerse-section-tr__content');
  // other properties here
  this.scrollingFn = false;
  this.scrolling = false;
  initImmerseSectionTr(this);
};

function initImmerseSectionTr(element) {
  updateMediaHeight(element); // update height of media element + set padding/margin values

  // observe when the element is sticky - update scale value and opacity layer 
  var observer = new IntersectionObserver(immerseSectionTrCallback.bind(element));
  observer.observe(element.media[0]);
};

function immerseSectionTrCallback(entries) {
  if(entries[0].isIntersecting) {
    if(this.scrollingFn) return; // listener for scroll event already added
    immerseSectionTrScrollEvent(this);
  } else {
    if(!this.scrollingFn) return; // listener for scroll event already removed
    window.removeEventListener('scroll', this.scrollingFn);
    this.scrollingFn = false;
  }
};

function immerseSectionTrScrollEvent(element) {
  // animate scale value here
};

По умолчанию элемент .immerse-section-tr__content не имеет background-color (мы используем класс .bg-transparent, чтобы установить его цвет bg на прозрачный).

Эффект наложения создается с помощью псевдоэлементов :: before / :: after. Их непрозрачность контролируется с помощью настраиваемого свойства CSS, которое можно обновить в JavaScript:

.immerse-section-tr__content {
  position: relative;

  &::after, // overlay layer
  &::before { // section background
    content: '';
    pointer-events: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    background: var(--color-bg);
    opacity: var(--immerse-section-tr-opacity, 0);
    pointer-events: none;
  }

  &::before {
    height: 100%;
    z-index: -1;
  }

  &::after {
    height: 100vh;
    transform: translateY(-100%);
  }
}

Когда элемент .immerse-section-tr__content входит в область просмотра, непрозрачность обновляется (с нуля до единицы) для создания фонового эффекта постепенного появления.


🔖 Выбор по тегам ×

Top