[Внешний словарь] Сравнение 5 вариантов реализации Rolling Ceiling [Улучшение производительности]

внешний интерфейс JavaScript

Измененный предварительный просмотр

Эта статья была написана три дня назад, и некоторые начальники дали мне несколько предложений по исправлению, которые я считаю действительно уместными. Итак, существует модифицированная версия этого обновления. Код обновляется синхронно с GitHub .

Модификации следующие:

  1. Добавлено графическое описание, интуитивно понятное описаниеgetBoundingClientRect()Значение коллекции
  2. Как избежать частых рисков перекомпоновки (оптимизировать мониторинг прокрутки)
  3. Отслеживайте проблемы с производительностью, вызванные прокруткой (используяIntersectionObserver, новый план)

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

предисловие

Первый запрос, который я получил, когда присоединился ко второй компании, заключался в том, чтобы отремонтировать эффект скользящего потолка, который раньше передавался на аутсорсинг. Мне было интересно, почему в натяжном потолке была ошибка, а потом я проверил код и обнаружил, что он был напрямую использованoffsetTopЭто свойство не выполняет обработку совместимости.

offsetTop

Используется для получения расстояния (значения смещения) текущего элемента до вершины позиционированного родителя ( element.offsetParent ).

целевой родительoffsetParentОпределение таково: родительский элемент с ближайшей позицией != static к текущему элементу.

Возможно, тот, кто писал этот код, не заметил подусловия «найти родителя».

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

содержание

  1. использоватьposition:stickyвыполнить
  2. Использование JQueryoffset().topвыполнить
  3. использовать роднойoffsetTopвыполнить
  4. использоватьobj.getBoundingClientRect().topвыполнить
  5. Оптимизация производительности

Знаете ли вы все вышеперечисленные четыре метода? Соответствующий код был загружен на GitHub , желающие могут клонировать код для локального запуска. Надеюсь дать звезду, чтобы поддержать его.

Четыре способа реализации

Давайте сначала посмотрим на визуализацию:

1. Используйтеposition:stickyвыполнить

1. Что такое липкое позиционирование?

липкое позиционированиеstickyЭквивалент относительного позиционированияrelativeи фиксированное позиционированиеfixedКомбинация ; в процессе прокрутки элементов страницы расстояние между элементом и его родительским элементом достигаетstickyкогда требуется липкое позиционирование; относительное позиционирование элементовrelativeЭффект становится фиксированным позиционированиемfixedЭффект.

Портал MDN

2. Как им пользоваться?

Условия использования:

  1. Родительский элемент не можетoverflow:hiddenилиoverflow:autoАтрибуты
  2. должен быть указанtop、bottom、left、rightОдно из 4 значений, иначе будет только относительное позиционирование
  3. Высота родительского элемента не может быть нижеstickyвысота элемента
  4. stickyЭлемент действует только внутри своего родительского элемента

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

.sticky {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
}

3. Полезно ли это свойство?

Давайте сначала посмотрим на совместимость этого свойства в Могу ли я использовать:

Видно, что совместимость этого свойства не очень хорошая, потому что этот API толькоэкспериментальные свойства. Однако совместимость этого API в системе IOS относительно хорошая.

Поэтому, если мы используем этот API в производственной среде, мы обычно используем его в сочетании со следующими методами.

2. Использование JQueryoffset().topвыполнить

Мы знаем, что JQuery инкапсулирует API для управления DOM и чтения вычисляемых свойств DOM на основеoffset().topЭтот API иscrollTop()Комбинируя, мы также можем добиться эффекта скользящего потолка.

...
window.addEventListener('scroll', self.handleScrollOne);
...
handleScrollOne: function() {
    let self = this;
    let scrollTop = $('html').scrollTop();
    let offsetTop = $('.title_box').offset().top;
    self.titleFixed = scrollTop > offsetTop;
}
...

Реализовать так можно, но так как JQuery потихоньку сходит со сцены истории, мы стараемся не использовать JQuery API в коде. Мы можем основываться наoffset().topИсходный код сам обрабатывает нативныеoffsetTop. Итак, есть третий способ.

SCROLLOTOP () имеет проблемы со совместимостью. В браузерах WECHAT, т. Е. И некоторые версии Firefox, значение $ ('html'). Scrolltop () будет 0, поэтому существует метод записи совместимости для третьей схемы.,

В-третьих, использование родногоoffsetTopвыполнить

мы знаемoffsetTopЭто смещение родительского элемента относительного позиционирования. Если элемент, который необходимо прокрутить вверх, появляется, чтобы найти родительский элемент, тоoffsetTopТо, что получается, — это не расстояние элемента от верха страницы.

мы можем правильноoffsetTopСделайте следующее:

getOffset: function(obj,direction){
    let offsetL = 0;
    let offsetT = 0;
    while( obj!== window.document.body && obj !== null ){
        offsetL += obj.offsetLeft;
        offsetT += obj.offsetTop;
        obj = obj.offsetParent;
    }
    if(direction === 'left'){
        return offsetL;
    }else {
        return offsetT;
    }
}

использовать:

...
window.addEventListener('scroll', self.handleScrollTwo);
...
handleScrollTwo: function() {
    let self = this;
    let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
    let offsetTop = self.getOffset(self.$refs.pride_tab_fixed);
    self.titleFixed = scrollTop > offsetTop;
}
...

Видите ли вы какие-то проблемы с двумя вышеуказанными методами?

мы должны использоватьscrollTop - offsetTopзначение для достижения эффекта натяжного потолка? ответ отрицательный.

Рассмотрим четвертый вариант.

4. Используйтеobj.getBoundingClientRect().topвыполнить

определение:

этоAPIможет рассказать вам об элементе на страницеОтносительное окно браузерарасстояние вверх и вниз.

использовать:
Вкладной потолок можно использоватьobj.getBoundingClientRect().topзаменятьscrollTop - offsetTop, код показан ниже:

// html
<div class="pride_tab_fixed" ref="pride_tab_fixed">
    <div class="pride_tab" :class="titleFixed == true ? 'isFixed' :''">
        // some code
    </div>
</div>

// vue
export default {
    data(){
      return{
        titleFixed: false
      }
    },
    activated(){
      this.titleFixed = false;
      window.addEventListener('scroll', this.handleScroll);
    },
    methods: {
      //滚动监听,头部固定
      handleScroll: function () {
        let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;
        this.titleFixed = offsetTop < 0;
        // some code
      }
    }
  }

Разница между offsetTop и getBoundingClientRect()

1. getBoundingClientRect():

Используется для получения положения левого, верхнего, правого и нижнего элемента на странице относительно окна браузера. Свернутая часть документа не включена.

Функция возвращаетobjectОбъект, имеет 8 свойств:top, right, buttom, left, width, height, x, y

2. offsetTop:

Используется для получения расстояния (значения смещения) текущего элемента до вершины позиционированного родителя ( element.offsetParent ).

целевой родительoffsetParentОпределение таково: родительский элемент с ближайшей позицией != static к текущему элементу.

offsetTopиoffsetParentметоды могут быть объединены, чтобы получить элементbodyРасстояние до верхнего поля. код показывает, как показано ниже:

getOffset: function(obj,direction){
    let offsetL = 0;
    let offsetT = 0;
    while( obj!== window.document.body && obj !== null ){
        offsetL += obj.offsetLeft;
        offsetT += obj.offsetTop;
        obj = obj.offsetParent;
    }
    if(direction === 'left'){
        return offsetL;
    }else {
        return offsetT;
    }
}

расширенные знания

смещениеШирина:

Количество места, занимаемого элементом в горизонтальном направлении:
offsetWidth = border-left + padding-left + width + padding-right + border-right

смещениеВысота:

Количество места, которое элемент занимает по вертикали:
offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom

Примечание: если есть вертикальная полоса прокрутки, offsetWidth также включает ширину вертикальной полосы прокрутки, если есть горизонтальная полоса прокрутки, offsetHeight также включает высоту горизонтальной полосы прокрутки;

смещениеВерх:

Верхняя внешняя граница элемента доoffsetParentРасстояние в пикселях между верхней и внутренней границами элемента;

смещениеСлева:

левая внешняя граница элемента доoffsetParentРасстояние в пикселях между левой внутренней границей элемента;

Меры предосторожности

  1. Все свойства смещения доступны только для чтения;
  2. Если элемент установленdisplay:none, то все его свойства смещения равны 0;
  3. Свойство смещения необходимо пересчитывать каждый раз при доступе к свойству смещения (сохранить переменную);
  4. При его использовании может случиться так, что DOM не инициализирован, а свойство прочитано, в это время оно вернет 0, для этой проблемы нам нужно дождаться завершения инициализации DOM-элемента перед его выполнением.

Столкнулись с двумя проблемами

1. Момент всасывания сопровождается встряхиванием

Причина дрожания в том, что: когда положение элемента потолка становится фиксированным, элемент отделяется от потока документов, и заполняется следующий элемент. Именно эта операция заполнения вызывает дрожание.

решение

Добавьте родительский элемент того же высоты на этот потолочный элемент, мы контролируем родительский элементgetBoundingClientRect().topзначение для достижения эффекта потолка, а именно:

<div class="title_box" ref="pride_tab_fixed">
    <div class="title" :class="titleFixed == true ? 'isFixed' :''">
    使用 `obj.getBoundingClientRect().top` 实现
    </div>
</div>

Это решение может устранить ошибку джиттера.

Во-вторых, эффект потолка не может вовремя среагировать

Эта проблема для меня головная боль, и раньше я не обращал на эту проблему внимания. Я не обращал внимания на эту проблему до тех пор, пока однажды не заказал еду на вынос в Meituan.

описывать:

  1. Когда страница прокручивается вниз, элемент потолка должен дождаться остановки прокрутки страницы, прежде чем появится эффект потолка.
  2. Когда страница прокручивается вверх, верхний элемент не возвращается в исходное положение при прокрутке к верхнему элементу для восстановления положения потока документа, но возвращается в исходное состояние после прекращения прокрутки страницы.

причина:В системе ios вы не можете отслеживать событие прокрутки прокрутки в режиме реального времени и запускать связанные с ним события, когда прокрутка останавливается.

решение:

Запомните первый вариантposition:sticky? Это свойство имеет хорошую совместимость в системах выше IOS6, поэтому мы можем различать устройства IOS и Android для выполнения двух видов обработки.

IOS использованиеposition:sticky, Android использует прослушиватель прокруткиgetBoundingClientRect().topзначение .

Что делать, если версия IOS слишком низкая? Вот идея:window.requestAnimationFrame().

Оптимизация производительности (новинка)

Это конец введения метода скользящего потолка в 4, но действительно ли это конец? На самом деле возможности для оптимизации еще есть.

Мы делаем оптимизацию производительности с двух направлений (на самом деле одного направления):

  1. Избегайте чрезмерного оплавления
  2. Оптимизация событий прослушивателя прокрутки

процесс локализации проблемы

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

Некоторые друзья могут сказать это, я знаю, но какое это имеет отношение к натяжному потолку?

Не волнуйтесь, помните ли вы, что потолок прокрутки использует offsetTop или getBoundingClientRect().top для получения смещения ответа?

Так как есть атрибуты прочитанных элементов, это естественно приведет к переформатированию страницы.

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

Оптимизация

Есть два решения этой проблемы:

  1. Принесение плавности в жертву производительности, использование регулирования для управления вызовом связанных методов
  2. Использование IntersectionObserver в сочетании с троттлингом также снижает плавность.

первый вариант

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

Это может контролировать только чтение в течение определенного периода времени

Здесь непосредственно используется функция дросселированияlodash.jsв упаковкеthrottleметод.

код показывает, как показано ниже:

window.addEventListener('scroll', _.throttle(self.handleScrollThree, 50));

Второй вариант

Второе решение относительно легко принять, то есть IntersectionObserver используется для поддержки IntersectionObserver, в противном случае используется дроссель.

Давайте сначала поговорим о IntersectionObserver

IntersectionObserver можно использовать для отслеживания того, входит ли элемент в видимую область устройства без частых вычислений, чтобы сделать это суждение.

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

Давайте посмотрим, как выглядит совместимость этого свойства:

Он поддерживает более 60%, и его еще можно использовать в проекте (нужно делать обработку на совместимость).

О том, как используется IntersectionObserver,см. MDNилиРуан Ифэн Учебник.

Код, оптимизированный с использованием IntersectionObserver и дросселя, выглядит следующим образом:

IntersectionObserverFun: function() {
    let self = this;
    let ele = self.$refs.pride_tab_fixed;
    if( !IntersectionObserver ){
        let observer = new IntersectionObserver(function(){
            let offsetTop = ele.getBoundingClientRect().top;
            self.titleFixed = offsetTop < 0;
        }, {
            threshold: [1]
        });
        observer.observe(document.getElementsByClassName('title_box')[0]);
    } else {
        window.addEventListener('scroll', _.throttle(function(){
            let offsetTop = ele.getBoundingClientRect().top;
            self.titleFixed = offsetTop < 0;
        }, 50));
    }
}, 

Уведомление

API IntersectionObserver является асинхронным и не срабатывает синхронно при прокрутке целевого элемента.

В спецификации указано, что реализация IntersectionObserver должна использовать requestIdleCallback(). Он не выполняет обратный вызов немедленно, он вызываетwindow.requestIdleCallback()Чтобы асинхронно выполнить нашу указанную функцию обратного вызова, а также указать максимальное время задержки 100 миллисекунд.

Суммировать:

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

Серия интерфейсных словарей

Серия «Front-end Dictionary» будет постоянно пополняться, и в каждом выпуске я буду рассказывать о точке знаний, которая появляется все чаще. Надеюсь, что в процессе прочтения в тексте будут неточности или ошибки, буду очень признательна, если что-то почерпну из этой серии, тоже буду очень рада.

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

Если вы хотите добавить групповое общение, вы также можете добавить маленького умного робота, который будет автоматически подтягивать вас в группу:

Портал популярных статей

  1. [Front-end Dictionary] Решение проблемы проникновения прокрутки
  2. Наследование (1) — Вы действительно понимаете цепочку прототипов?
  3. [Внешний словарь] Сетевая основа необходима для продвинутых (1)
  4. [Внешний словарь] Сетевая основа необходима для продвинутых (ниже)
  5. 【Внешний словарь】Перспективное мышление, вызванное реализацией снежного фона Canvas
  6. [Внешний словарь] Какие ссылки кэша задействованы от ввода URL до отображения (очень подробно)