Страницы

Поиск по вопросам

среда, 18 декабря 2019 г.

Как реализовать такой эффект параллакса?

#javascript #html #jquery #css


На сайте Apple (блок "Самый мощный iMac в истории") есть эффект параллакса. При прокрутке
до определенной точки картинка начинает прокручиваться медленнее текста, и в конце
медленная прокрутка сменяется стандартной. Как можно реализовать такой же эффект?

UPD:

Есть скрипт, который при прокрутке до определенной высоты замедляет прокрутку блока.
Сначала у нас блок с абсолютным позиционированием, при прокрутке определенного количества
пикселей блок получает фиксированное позиционирование и начинает прокручиваться медленнее
основного контента.

        // Наш элемент, который будет замедляться
var $element = $('.first'),
    /*
    * Последняя позиция скролла, нужна
    * для вычисления
    */
    lastScrollTop = 0,
    /*
    * Отступ сверху для нашего блока 
    * с фиксированным и абсолютным
    * позиционированием
    */
    defaultElementAbsoluteTop = 150,
    defaultElementFixedTop = 90,
    // Коэффициэнт замедления
    n = 4;

    // Отслеживаем скролл страницы
    $(window).scroll(function (event) {

    // Вычисляем текущее положение
    var scrollTop = $(window).scrollTop(),
        /*
        * Вычисляем положение нашего блока
        * от верхней части сайта/родителя.
        * Т.к. этот элемент до этого был абсолютным,
        * то у него отступ сверху всегда одинаков,
        * А когда подходит момент, когда нужно его фиксировать
        * отступ расчитывается от последнего, который был
        * 150 пикселей. Это неправильно. Я ввел проверку,
        * Если отступ стандартный абсолютный равен
        * текущему, то, при фиксировании блока
        * ставим ему оступ фиксированного блока
        * и отсюда начинаются правильные расчеты
        */
        elementTop = $element.position().top == defaultElementAbsoluteTop ? defaultElementFixedTop
: $element.position().top;

    if(scrollTop < 60) 
    {
        /*
        * Проскролили меньше, чем на 60 пикселей
        * То оставляем абсолютное позиционирование
        */
        $element.removeClass('fixed');
        // И ставим элементу абсолютный отступ сверху
        $element.css({
            top: defaultElementAbsoluteTop
        });
    }
    else
    {
        // Фиксируем элемент
        $element.addClass('fixed');
        // TODO: удалить!
        //return;

        /*
        * Если прошлое положение меньше,
        * чем текущее, значит двигаемся <вниз>
        */
        if (scrollTop > lastScrollTop){
            // Вычисляем разницу перемещения
            var diff = scrollTop - lastScrollTop;
            // Даем отступ сверху в n раз меньше, чем скролл
            $element.css({
                top: elementTop - diff/n + 'px'
            });
        // В противном случае, двигаемся <вверх>
        } else {
            // Вычисляем разницу перемещения
            var diff = lastScrollTop - scrollTop;
            /*
            * Вычисляем результирующее положение нашего элемента,
            * с учетом замедления n
            */
            resultElementTop = elementTop + diff/n;
            /*
            * Если конечный результат вышел больше,
            * чем отступ сверху по умолчанию,
            * то даем стандартный отступ.
            * Иначе даем результирующий отступ
            */
            resultElementTop = resultElementTop > defaultElementFixedTop ? defaultElementFixedTop
: resultElementTop;
            // Позиционируем элемент
            $element.css({
                top: resultElementTop + 'px'
            });
        }
    }
    // Запоминаем последнюю позицию скролла
    lastScrollTop = scrollTop;
});


Нужно, чтобы блок сначала был с position: absolute, при прокрутке до определенного
участка блок получает position:fixed и прокручиваетс медленнее. При прокрутке страницы
ещё ниже, блок опять становится абсолютно спизиционированным. При рокрутке обратно
вверх блок получает фиксированное позиционирование и прокручивается вниз с того момента,
на котором остановился при прокрутке вверх.
    


Ответы

Ответ 1



Поковырялся, вот что получилось. firstPosScroll = []; // позиция курсора, с которой объект видим windowH = $(window).height(); // высота окна getPosScroll(); // заполнение вышеуказанных позиций $(window).resize(function() { // при изменении размера окна пересчитываем позиции getPosScroll(); }) $(window).scroll(function() { //обработчик скролла posScroll = $(this).scrollTop(); // текущая позиция курсора posMiddle = windowH / 2; // середина окна $('.prlx').each(function(i, v) { // для каждого объекта offsetObj = $(this).offset().top; // положение объекта относительно верха документа posObj = offsetObj - posScroll; // текущая позиция объекта koef = 1 - ($(this).find('.logo').height() / $(this).find('.desc').height()); // коэффициент замедления обратно пропорционален разнице высот картинки и описания if (posObj < posMiddle && posObj + $(this).height() > posMiddle) { //условие видимости объекта $(this).find('.logo').css({ 'top': (posScroll - firstPosScroll[i] - posMiddle) * koef + 'px' }); // задаем высоту } }); }); function getPosScroll() { $('.prlx').each(function(i, v) { firstPosScroll[i] = $(this).offset().top - windowH < 0 ? 0 : $(this).offset().top - windowH; }); } section {position: relative;} section.left-logo>div.logo {left: 0;} section.left-logo>div.desc {float: right;width: 50%;} section.right-logo>div.logo {right: 0;} div.logo {position: absolute;width: 50%;} section.right-logo>div.desc {float: left;width: 50%;} header {height: 800px;background: #e6e6e6;} img {max-width: 100%;} div.p {margin: 20px 0;} div.clear {clear: both;width: 100%;}


Ответ 2



Мой вариант решения - пример здесь. Описание всего, что происходит, в принципе, есть в коде ниже. Отчасти использовал идею со страницы Apple, но обошелся без translate3D. Основной HTML:
Основной CSS: .content { position: relative; } .slow { position: absolute; top: 100px; } JS: var $window = $(window); var $parent = $('.content').eq(1); var $slow = $('.slow'); // сохраняем первоначальную позицию блока по отношению к родителю (100px) var initialTop = $slow.position().top; // эффект замедления // то, на сколько пикселей будет двигаться блок при position: fixed var translate = 1; // указываем расстояние в пикселях до/после родителя, чтобы знать // когда добавлять/убирать position: fixed // могут быть равны 0, тогда определяться будет по позиции родителя var parentBefore = 300, parentAfter = 100; // последнее значение $window.scrollTop(), для определения направления скролла var previousScrollTop = 0; // направление по умолчанию - вниз var direction = -1; // временные переменные var parentTop = null, parentBot = null; function onScroll() { // узнаем в каком направлении мы сейчас скроллим // "1" - вверх, "-1" - вниз direction = previousScrollTop > $window.scrollTop() ? 1 : -1; previousScrollTop = $window.scrollTop(); // вычисляем верхнюю и нижнюю границу того, когда нам нужно начинать эффект // верхняя граница: когда топ родителя + запас "before" достигает топа окна // нижняя граница: когда низ родителя + запас "after" появляется снизу var borderTop = $parent.offset().top - parentBefore - $window.scrollTop(); var borderBot = borderTop + parentAfter + $parent.height(); // если мы внутри границ if (borderBot > $window.height() && borderTop <= 0) { // достаем текущее положение элемента относительно окна var currentTop; // если скролим вверх, то при первом переключении на position: fixed, // восстанавливаем сохраненное значение (см. parentTop) if ($slow.css('position') === 'absolute') { currentTop = parentTop; } else { // иначе берем текущую фиксированную позицию currentTop = parseInt($slow.css('top'), 10); } // вычисляем новое значение со сдвигом в зависимости от направления var nextTop = currentTop + translate * direction; // проверка для того, чтобы не вылезать за пределы стартовой позиции if (nextTop < initialTop) { nextTop = initialTop; } // когда впервые попадаем в границы, добавляем запас "before" к позиции if (!parentTop) { nextTop += parentBefore; } $slow.css('position', 'fixed'); $slow.css('top', nextTop); $slow.css('bottom', 'auto'); // сохраняем позицию относительно окна сверху parentTop = $slow.position().top; // сохраняем позицию относительно окна снизу parentBot = $window.height() - $slow.position().top - $slow.height(); // если вышли за границу снизу, цепляем блок к низу родителя } else if (borderBot < $window.height()) { $slow.css('position', 'absolute'); $slow.css('top', 'auto'); // устанавливаем позицию блока относительно родителя из сохраненного // значения (см. parentBot), с вычетом запасов "before" и "after" $slow.css('bottom', parentBot - parentAfter + parentBefore + 'px'); // если вышли за границу сверху, восстанавливаем все состояния } else if (borderTop > 0) { $slow.css('position', 'absolute'); $slow.css('top', initialTop); $slow.css('bottom', 'auto'); parentTop = null; parentBot = null; } } $window.on('scroll', onScroll); Замеченные недостатки: нельзя замедлять перемещение фиксированного блока меньше чем на 1px (переменная translate) позиция блока при скролле всегда рандомная, т.к. перемещение выполняется при каждом событии scroll. Правка: JSBin в Firefox не видел функцию onScroll, которая была объявлена под $window.on('scroll', onScroll);. Также Firefox даже если у элемента стоит top:auto, возвращает числовое значение для вызова $el.css('top'), а не строку auto.

Ответ 3



Вот мой вариант: HTML:
CSS: .about .parallax { height: 400px; overflow: hidden; position: relative; } .about .parallax img { width: 100%; position: absolute; } JS: function parallax() { var $block = $('.parallax'); var $window = $(window); for (var i = 0; i<$block.length; i++) { var $item = $($block[i]), docViewTop = $window.scrollTop(), docViewBottom = docViewTop + $window.height(), elemTop = $item.offset().top, elemBottom = elemTop + $item.height(), img = $item.find('img'), heightParallax = img.height() - $block.height(); if ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)) { var show = docViewBottom - elemTop; var par = heightParallax / ($window.height() + $block.height()); var top = par * show * -1; if (top > 0) { img.css('top', '0px'); } else if (heightParallax < top * -1) { img.css('top', heightParallax + 'px'); } else { img.css('top', top + 'px'); } } else if (elemTop >= docViewBottom) { img.css('top', '0px'); } else if (elemTop <= elemTop) { img.css('top', heightParallax + 'px'); } } } $(window).scroll(function() { parallax(); }).resize(function() { parallax(); }); Результат: Piggy refer

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

Отправить комментарий