предисловие
Головная навигация в предыдущем проекте нужна была для достижения эффекта потолка. Сначала я это реализовал сам, но обнаружил, что эффект всегда был немного хуже. В то время я торопился реализовать функцию.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