предисловие
Головная навигация в предыдущем проекте нужна была для достижения эффекта потолка. Сначала я это реализовал сам, но обнаружил, что эффект всегда был немного хуже. В то время я торопился реализовать функцию.react-stickyЭта библиотека, когда у меня есть время, я думаю о том, чтобы тщательно обдумать эту проблему с потолком.
1. Липкое позиционирование
Эффект потолка, естественно, придет на умposition:sticky
, В Интернете также есть много связанной информации об этом объекте, вы можете проверить это самостоятельно. Просто упомянем одну вещь, которая отличалась от того, что я изначально ожидал:
Пример 1. В соответствии с моими ожиданиями, нормальный потолок
// html
<body>
<div class="sticky">123</div>
</body>
// css
body {
height: 2000px;
}
div.sticky {
position: sticky;
top:0px;
}
Пример 2. Не соответствует моим ожиданиям Не может потолок
// html
<body>
<div class='sticky-container'>
<div class="sticky">123</div>
</div>
</body>
// css
body {
height: 2000px;
}
div.sticky-contaienr {
height: 1000px; // 除非加上这段代码才会有一定的吸顶效果
}
div.sticky {
position: sticky;
top:0px;
}
Я думал просто добавитьposition:sticky
, уже настроенtop
Значение , может быть потолком, вне зависимости от других элементов, это как раз тот эффект, который мне нужен, как в примере 1.
Но на самом деле дляposition:sticky
Другими словами, его область действия может быть только внутри родительского элемента, и если он прокручивается за пределы родительского элемента, он не может быть высосан наверх. В примере 2.sticky-container
высота и.sticky
Высота одинаковая, при прокрутке нет эффекта потолка. Дать.sticky-container
настраивать1000px
высота, что.sticky
может быть там1000px
Роллинг потолок.
Конечноsticky
Это сделано для достижения более сложных эффектов.
Прикрепить ссылкуCSS Position Sticky - How It Really Works!
2. react-sticky
2.1 Использование
// React使用
<StickyContainer style={{height: 2000}}>
<Sticky>
{({style}) => {
return <div style={style}>123 </div> // 需要吸顶的元素
}}
</Sticky>
其它内容
</StickyContainer>
// 对应生成的Dom
<div style='height: 2000px;'> // sticky-container
<div> // parent
<div style='padding-bottom: 0px;'></div> // placeholder
<div>123 </div> // 吸顶元素
</div>
其它内容
</div>
2.2 Сомнение
Глядя на приведенный выше код React и соответствующую сгенерированную структуру dom, мы обнаруживаем, чтоSticky
Генерируется вложенная структура div, оборачивающая элементы, которые нам действительно нужны, к потолку:
<div> // parent
<div style='padding-bottom: 0px;'></div> // placeholder
<div>123 </div> // 吸顶元素
</div>
Сначала я был немного озадачен, почему эта библиотека реализована именно так, не может ли она сгенерировать следующую структуру? минусdiv1
,div2
?
<div style='height: 2000px;'>
<div>123 </div>
其它内容
</div>
Так что мне было наплевать на чужой код, я написал демку локально, и думал, как добиться эффекта потолка, а потом потихоньку разобралсяreact-sticky
дизайн.
2.3 Путаница
потолок, то есть когда页面滚动的距离
Превосходить吸顶元素距离文档(而非浏览器窗口)顶部的高度
Когда это так, потолочный элемент будет потолком, в противном случае потолочный элемент станет обычным позиционированием потока документов.
Так что, конечно, вы можете第一次
Перед прокруткой пропустите элемент потолка (позже будет заменен на липкий)sticky.getBoundingClientRect().top
Получите расстояние элемента от верхней части html-документа, которое должно бытьhtmlTop
, причина, по которой он выделяется перед первой прокруткой, заключается в том, что только перед первой прокруткой отображается расстояние от верхней части HTML-документа, а после этого она может представлять только расстояние от верхней части окна браузера.
пройти черезdocument.documentElement.scrollTop
Получите предполагаемое расстояние прокрутки страницыscrollTop
, вычисляется каждый раз, когда срабатывает событие прокруткиscrollTop - htmlTop
, больше 0,sticky元素的position
установить какfixed
, в противном случае он вернется в исходный режим позиционирования.
Это нормальный потолочный отсос, но будет проблема, т.к.sticky
сталиfixed
Вне потока документов, что приводит к отсутствующей части содержимого документа. Представлять себе:
div1
div2
div3
1,2,3三个div,假如突然2变为fixed了,那么会变成:
div1
div3 div2
То есть после обсоса потолка содержимое div3 будет заблокировано div2.
Так что проверьте прямо сейчасreact-sticky
В сгенерированном доме потолочный элемент будет иметь родственный элементplaceholder
. С заполнителем, даже если потолочный элементfixed
Чтобы выйти из потока документов, есть также заполнитель, который занимает свое место:
div1
placeholder div2
div3
В то же время, поскольку к потолочному элементу добавляется родственный элемент, лучший способ справиться с ним — добавить еще одинparent
Оберните два элемента так, чтобы они не подвергались легкому влиянию других элементов и не подвергались легкому влиянию других элементов (наверное).
2.4 Исходный код
Его реализация также очень проста, достаточноSticky.js
а такжеContainer.js
Два файла, поговорим об этом немного. Код не вставлен, нажмите здесь, чтобы увидетьContainer.js, Sticky.js.
- Сначала привяжите пакет событий:
resize
,scroll
,touchstart
,touchmove
,touchend
,pageshow
,load
. - Через шаблон наблюдателя, когда запускаются вышеуказанные события,
Container
информация о местоположении передаетсяSticky
на компоненте. -
Sticky
Затем компонент определяет, нужен ли он, вычисляя информацию о местоположении.fixed
позиция.
На самом деле, это так, конечно, это также поддерживаетrelative
,stacked
Есть два режима, поэтому код усложняется. Посмотрите, чему мы можем научиться из этого:
- использовал
raf
библиотека для управления анимацией, это правильноrequestAnimationFrame
Обработка совместимости - использовать для
Context
, и шаблон наблюдателя - Мне на самом деле нужно отслеживать очень много событий (в любом случае, я добавлю только прокрутку)
- использовать
React.cloneElement
создавать элементы так, чтобы конечное использованиеSticky
Компоненты кажутся немного необычными в использовании. -
disableHardwareAcceleration
Свойство используется для отключения аппаратного ускорения анимации, по сути решая, следует ли устанавливатьtransform:"translateZ(0)";
Заинтересованы в последней точке знаний, всегда говорите использоватьtransform
Может запустить аппаратное ускорение, анимация плавнее, это правда? Поэтому я снова искал информацию.An Introduction to Hardware Acceleration with CSS Animations. проверить хром локальноperformance
для открытияleft,top
,fps,gpu,frames
Дождитесь появления зеленой столбчатой линии, используйтеtransform
Есть только несколько зеленых столбчатых линий. Это имеет смысл.
3. Приходите к простому варианту
Разобравшись с исходным кодом, напишите (скопируйте) его самостоятельно и реализуйте только самую простую потолочную функцию:
import React, { Component } from 'react'
const events = [
'resize',
'scroll',
'touchstart',
'touchmove',
'touchend',
'pageshow',
'load'
]
const hardwareAcceleration = {transform: 'translateZ(0)'}
class EasyReactSticky extends Component {
constructor (props) {
super(props)
this.placeholder = React.createRef()
this.container = React.createRef()
this.state = {
style: {},
placeholderHeight: 0
}
this.rafHandle = null
this.handleEvent = this.handleEvent.bind(this)
}
componentDidMount () {
events.forEach(event =>
window.addEventListener(event, this.handleEvent)
)
}
componentWillUnmount () {
if (this.rafHandle) {
raf.cancel(this.rafHandle)
this.rafHandle = null
}
events.forEach(event =>
window.removeEventListener(event, this.handleEvent)
)
}
handleEvent () {
this.rafHandle = raf(() => {
const {top, height} = this.container.current.getBoundingClientRect()
// 由于container只包裹着placeholder和吸顶元素,且container的定位属性不会改变
// 因此container.getBoundingClientRect().top大于0则吸顶元素处于正常文档流
// 小于0则吸顶元素进行fixed定位,同时placeholder撑开吸顶元素原有的空间
const {width} = this.placeholder.current.getBoundingClientRect()
if (top > 0) {
this.setState({
style: {
...hardwareAcceleration
},
placeholderHeight: 0
})
} else {
this.setState({
style: {
position: 'fixed',
top: '0',
width,
...hardwareAcceleration
},
placeholderHeight: height
})
}
})
}
render () {
const {style, placeholderHeight} = this.state
return (
<div ref={this.container}>
<div style={{height: placeholderHeight}} ref={this.placeholder} />
{this.props.content(style)}
</div>
)
}
}
//使用
<EasyReactSticky content={style => {
return <div style={style}>this is EasyReactSticky</div>
}} />
Очевидно, что большая часть кода заимствуетreact-sticky
, уменьшая код конфигурации параметра и для двух режимовstacked
а такжеrelative
служба поддержки. Это действительно просто, и в то же время форма вызова компонента изменена, аrender-props
.
4. Резюме
Эта статья возникает из-за небольшой дыры, оставленной предыдущей работой, стремящейся выполнить задачу, но, к счастью, теперь она заполнена.react-sticky
на гитхабе1926
Сама по себе звезда не сложна, и можно многому научиться, прочитав такую маленькую библиотеку, которая выдержала испытание открытым исходным кодом.
5. Обсуждение
- Если вы дадите использование
top,left
Измените элемент на анимацию, например.An Introduction to Hardware Acceleration with CSS AnimationsВ первом примере добавьтеtransform:translateZ(0)
, Будет ли аппаратное ускорение? (Результат моего теста вроде никакой, впереди еще много покраски)
Добро пожаловать на обсуждение ~
использованная литература
react-sticky
CSS Position Sticky - How It Really Works!
An Introduction to Hardware Acceleration with CSS Animations
render-props