В связи с потребностями проекта я недавно начал разрабатывать паблик аккаунт WeChat и соприкоснулся с фреймворком Vue.Хотя реализация этого эффекта основана на фреймворке Vue, его можно позаимствовать и из других мест. принцип тот же.
Войдите в тему, сначала посмотрите на рендеры:
На самом деле, в js довольно просто сделать этот эффект, потому что в css мы можем установить элементposition: fixed;
, чтобы его можно было зафиксировать там, так что независимо от того, как прокручивается страница, ее положение не будет затронуто, поэтому наша идея состоит в том, чтобы установить атрибут position элемента заголовка, который должен быть зафиксирован в нужное время. Но когда наступает подходящее время? Это требует от нас расчетов. Нам нужно следить за состоянием прокрутки страницы. Когда страница прокручивается до позиции, где должен быть верхний элемент, мы устанавливаем его фиксированным, поэтому нам нужно НАС:
1. Следите за состоянием прокрутки страницы:
Добавьте следующий код в смонтированный обратный вызов:
mounted() {
// handleScroll为页面滚动的监听回调
window.addEventListener('scroll', this.handleScroll);
},
Также удалите слушателя в обратном вызове уничтожения:
destroyed(){
window.removeEventListener('scroll', this.handleScroll);
},
2. Рассчитайте расстояние от потолочного элемента до верха страницы:
Рассчитав это расстояние, вы можете определить время фиксации потолочного элемента.Если высота элемента над вашим потолочным элементом фиксирована, то это просто.Вы можете судить об этом непосредственно в методе handleScroll, а можете напрямую прыгать.В третий шаг, если он динамический, то нам нужно запросить данные по интерфейсу и выполнить динамический расчет после рендеринга элемента dom.В Vue есть очень полезный метод, который может легко контролировать завершение рендеринга dom:
// 监听dom渲染完成
this.$nextTick(function(){
// 这里fixedHeaderRoot是吸顶元素的ID
let header = document.getElementById("fixedHeaderRoot");
// 这里要得到top的距离和元素自身的高度
this.offsetTop = header.offsetTop;
this.offsetHeight = header.offsetHeight;
console.log("offsetTop:" + this.offsetTop + "," + this.offsetHeight);
});
3. Определите расстояние прокрутки страницы:
handleScroll(){
// 得到页面滚动的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
// 判断页面滚动的距离是否大于吸顶元素的位置
this.headerFixed = scrollTop > (this.offsetTop - this.offsetHeight * 2);
},
ps: Теоретически это должно быть scrollTop > (this.offsetTop - this.offsetHeight), но я не знаю, почему scrollTop все еще выше, чем значение this.offsetTop - this.offsetHeight, когда я прокручиваю до верхней позиции элемента после того, как я сделал это здесь Small, поэтому здесь * 2, значение, полученное таким образом, в самый раз, если кто-нибудь, кто знает, может помочь решить головоломку.
Выше мы получили значение логического атрибута headerFixed, далее нам просто нужно установить элемент потолка в соответствии с его значениемposition: fixed;
свойства в порядке.
Мы можем написать стиль css:
.isFixed{
position: fixed;
top: px2rem(110);
left: px2rem(20);
right: px2rem(20);
}
Затем Vue может динамически установить класс в элементе dom вот так, что очень удобно:
<div id="fixedHeaderRoot">
<div id="knowPointHeader" class="knowPointHeader" :class="headerFixed?'isFixed':''">
<div><span>知识模块</span></div>
<div><span>知识点</span></div>
<div><span>能力要求</span></div>
</div>
</div>
На самом деле этот эффект здесь достигнут, но я обнаружил в ходе теста, что из-за того, что когда страница ios mobile прокручивается вниз, ее также можно подтянуть вверх, возникает эффект резиновой ленты, этот эффект вызовет ошибку на нашем страница, Из-за эффекта резиновой ленты он также запускает мониторинг прокрутки страницы. Когда данных много, вы не можете их увидеть. Только когда данные просто заполняют экран, а затем вы продолжаете скользить экран вверх, страница будет запущена. В это время значение scrollTop, вычисленное в методе handleScroll, больше, чем расстояние до верхней части элемента потолка, поэтому элемент потолка будет установлен как фиксированный атрибут. Мы все знаем что после того, как элемент установлен вposition: fixed;
, то он будет позиционирован относительно окна браузера, так что контент под нами будет идти вверх, поэтому значение scrollTop меньше, чем расстояние до вершины верхнего элемента, поэтому свойство headerFixed снова равно false,position: fixed;
Атрибут снова исчез, поэтому он позиционируется относительно своего исходного родительского элемента, поэтому он становится циклом, вы обнаружите, что страница будет прыгать вверх и вниз, это определенно невозможно, поэтому я снова обращусь к этой проблеме. оптимизация, конечно, это решение не кажется особо идеальным, но оно действительно может решить эту проблему.
Благодаря приведенному выше анализу мы можем узнать, что причина этой проблемы в том, что мы установили элементposition: fixed;
атрибут, чтобы содержимое снизу было выдвинуто вверх, поэтому для решения этой проблемы мы не будем исправлять этот элемент, но в этом случае не будет достигнут эффект засасывания потолка, поэтому нам нужно добавить элемент, который является именно такой же, как у потолочного всасывающего элемента, который всегда находится в фиксированном состоянии:
<div id="fixedHeaderRootReal">
<div class="knowPointHeader isFixed" v-show="headerFixed">
<div><span>知识模块</span></div>
<div><span>知识点</span></div>
<div><span>能力要求</span></div>
</div>
</div>
Этот элемент скрыт по умолчанию. Мы отображаем его только тогда, когда расстояние прокрутки страницы достигает его положения. Поскольку он находится в фиксированном состоянии, его скрытое отображение не повлияет на страницу, поэтому следующий контент Если вы не идете вверх, вы можете решить ошибку эффекта резиновой ленты на мобильном телефоне ios. Конечно, этот метод немного сложен, но на данный момент лучшего решения нет. Если у вас есть лучшее решение, добро пожаловать в комментарий ниже . Наконец, позвольте мне показать вам макет моей страницы:
<div v-show="kpointListShow" class="knowPointList">
<div id="fixedHeaderRoot">
<div id="knowPointHeader" class="knowPointHeader">
<div><span>知识模块</span></div>
<div><span>知识点</span></div>
<div><span>能力要求</span></div>
</div>
</div>
<div id="fixedHeaderRootReal">
<div class="knowPointHeader isFixed" v-show="headerFixed">
<div><span>知识模块</span></div>
<div><span>知识点</span></div>
<div><span>能力要求</span></div>
</div>
</div>
<div class="knowPointItem" v-for="(kpointItem,index) in rows.kpointList" :key="index">
<div><span>{{kpointItem.knowModule}}</span></div>
<div><span>{{kpointItem.knowPoint}}</span></div>
<div><span>{{kpointItem.abilityRequire}}</span></div>
</div>
</div>