В этом уроке мы покажем вам, как создать эффект погружения между секциями.
Мы можем начать создавать два блочных элемента, один из которых используется для медиа-элемента (например, видео / изображения), а другой - для общего контента.
Во время прокрутки медиа-элемент должен быть зафиксирован, когда он достигнет центра области просмотра. С этого момента он должен масштабироваться до анимации в полноэкранном элементе. После завершения анимации блок содержимого должен начать прокручиваться по ней.
<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 пикселей. Это потребует огромного количества дополнительной прокрутки и пустого пространства под активом, что повлияет на удобство использования.
Для завершения анимации нам еще необходимо:
- Обнаруживать, когда медиа-элемент входит в область просмотра, чтобы вызвать масштабную анимацию, используя API-интерфейс Intersection Observer;
- Анимировать масштаб на основе значения прокрутки, используя событие прокрутки окна;
- Удалите прослушиватель событий прокрутки, как только элемент покинет область просмотра.
Мы можем создать объект 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
входит в область просмотра, непрозрачность обновляется (с нуля до единицы) для создания фонового эффекта постепенного появления.