如何让部分变得粘稠直到用户滚动特定长度
How to get section to become sticky until the user has scrolled a specific length
我正在尝试复制类似于 Postmates fleet website 的东西,他们在一个部分上使用 position: sticky
并更改该部分中的元素,直到用户滚动浏览所有内容。
这是我的意思的一个例子:
到目前为止,我已经在我想置顶的部分设置了一个ref
:
ref={(r) => this.ref = r}
...
/>
然后我在页面加载时获取容器的高度:
componentDidMount() {
if (this.ref) {
this.setState({ targetY: this.ref.getBoundingClientRect().bottom },
// tslint:disable-next-line:no-console
() => console.log(this.state, 'state'))
}
window.addEventListener('scroll', this.handleScroll)
}
之后我检测到页面的滚动并知道该部分何时可见:
handleScroll(e) {
const scrollY = window.scrollY + window.innerHeight;
const { lockedIntoView, targetY } = this.state;
// tslint:disable-next-line:no-console
console.log(scrollY, "scrollY");
if (scrollY >= targetY) {
this.setState({ lockedIntoView: true }, () => {
// tslint:disable-next-line:no-console
console.log(`LockedIntoView: ${lockedIntoView}`);
});
} else {
this.setState({ lockedIntoView: false }, () => {
// tslint:disable-next-line:no-console
console.log(`LockedIntoView: ${lockedIntoView}`);
});
}
}
在将容器设置为粘性位置之前:
<section
...
style={{position: lockedIntoView ? "sticky" : ""}}
...
</>
我想知道如何让它像 postmates 网站一样,在内容滚动之前(或现在,直到用户滚动到指定高度),该部分才保持在屏幕的完整视图中)?
或者只是了解他们是如何做到的,以及我需要采取哪些步骤才能复制它?
这是我的 Codesandbox
需要考虑的几件事:
1) 当您将目标设置为 position: fixed
时,您会将其从 dom 流中移除,因此您需要通过向 dom 添加一些高度来进行补偿 - 示例下面在 body
.
上执行此操作
2)检查scrollY时需要考虑目标的高度
3) 当您超过目标高度时,您将目标重新添加到 dom 流程中并删除在步骤 1 中添加的额外高度。
4) 如果我们向上或向下滚动,您需要保持跟踪 - 这已经完成,但比较最后滚动位置和当前位置。
有关粗略示例,请参阅下面的内嵌评论。
styles.css:
.lockedIntoView {
position: fixed;
top: 0;
}
index.js
import * as React from "react";
import { render } from "react-dom";
import "./styles.css";
interface IProps {
title: string;
content: string;
}
interface IHeroState {
targetY: number;
lockedIntoView: boolean;
}
let lastScrollY = 0;
class App extends React.Component<IProps, IHeroState> {
ref: HTMLElement | null;
constructor(props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
this.state = {
lockedIntoView: false,
targetY: 0
};
}
componentDidMount() {
if (this.ref) {
this.setState(
{ targetY: this.ref.getBoundingClientRect().bottom },
// tslint:disable-next-line:no-console
() => console.log(this.state, "state")
);
}
window.addEventListener("scroll", this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScroll);
}
handleScroll(e) {
const scrollY = window.scrollY + window.innerHeight;
const { lockedIntoView, targetY } = this.state;
if (lockedIntoView) {
console.info("we locked");
// update the padding on the doc as we now removing the target from normal flow
window.document.body.style.paddingBottom = `${this.ref.getBoundingClientRect().height}px`;
// we passed the taret so reset - we have to factor the target height in the calc
if (scrollY > targetY + this.ref.getBoundingClientRect().height) {
window.document.body.style.paddingBottom = "0px";
this.setState({ lockedIntoView: false });
}
} else {
// if we scrollign down and at the target, then lock
if (
scrollY > lastScrollY &&
(scrollY >= targetY &&
scrollY < targetY + this.ref.getBoundingClientRect().height)
) {
console.info("we locked");
this.setState({ lockedIntoView: true });
}
}
// update last scroll position to determine if we going down or up
lastScrollY = scrollY;
}
render() {
const { lockedIntoView, targetY } = this.state;
const fixed = lockedIntoView ? "lockedIntoView" : "";
return (
<div className="App">
<div className="bg-near-white min-vh-100 ">
<h1>First section</h1>
</div>
<div
ref={r => (this.ref = r)}
className={`vh-100 bg-blue pa0 ma0 ${fixed}`}
>
<h2>
When this is in full view of the window, it should remain fixed
until the window has scrolled the full length of the window
</h2>
</div>
<div style={{ height: "300vh" }} className="bg-near-white min-vh-100 ">
<h2>The next section</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
我正在尝试复制类似于 Postmates fleet website 的东西,他们在一个部分上使用 position: sticky
并更改该部分中的元素,直到用户滚动浏览所有内容。
这是我的意思的一个例子:
到目前为止,我已经在我想置顶的部分设置了一个ref
:
ref={(r) => this.ref = r}
...
/>
然后我在页面加载时获取容器的高度:
componentDidMount() {
if (this.ref) {
this.setState({ targetY: this.ref.getBoundingClientRect().bottom },
// tslint:disable-next-line:no-console
() => console.log(this.state, 'state'))
}
window.addEventListener('scroll', this.handleScroll)
}
之后我检测到页面的滚动并知道该部分何时可见:
handleScroll(e) {
const scrollY = window.scrollY + window.innerHeight;
const { lockedIntoView, targetY } = this.state;
// tslint:disable-next-line:no-console
console.log(scrollY, "scrollY");
if (scrollY >= targetY) {
this.setState({ lockedIntoView: true }, () => {
// tslint:disable-next-line:no-console
console.log(`LockedIntoView: ${lockedIntoView}`);
});
} else {
this.setState({ lockedIntoView: false }, () => {
// tslint:disable-next-line:no-console
console.log(`LockedIntoView: ${lockedIntoView}`);
});
}
}
在将容器设置为粘性位置之前:
<section
...
style={{position: lockedIntoView ? "sticky" : ""}}
...
</>
我想知道如何让它像 postmates 网站一样,在内容滚动之前(或现在,直到用户滚动到指定高度),该部分才保持在屏幕的完整视图中)?
或者只是了解他们是如何做到的,以及我需要采取哪些步骤才能复制它?
这是我的 Codesandbox
需要考虑的几件事:
1) 当您将目标设置为 position: fixed
时,您会将其从 dom 流中移除,因此您需要通过向 dom 添加一些高度来进行补偿 - 示例下面在 body
.
2)检查scrollY时需要考虑目标的高度
3) 当您超过目标高度时,您将目标重新添加到 dom 流程中并删除在步骤 1 中添加的额外高度。
4) 如果我们向上或向下滚动,您需要保持跟踪 - 这已经完成,但比较最后滚动位置和当前位置。
有关粗略示例,请参阅下面的内嵌评论。
styles.css:
.lockedIntoView {
position: fixed;
top: 0;
}
index.js
import * as React from "react";
import { render } from "react-dom";
import "./styles.css";
interface IProps {
title: string;
content: string;
}
interface IHeroState {
targetY: number;
lockedIntoView: boolean;
}
let lastScrollY = 0;
class App extends React.Component<IProps, IHeroState> {
ref: HTMLElement | null;
constructor(props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
this.state = {
lockedIntoView: false,
targetY: 0
};
}
componentDidMount() {
if (this.ref) {
this.setState(
{ targetY: this.ref.getBoundingClientRect().bottom },
// tslint:disable-next-line:no-console
() => console.log(this.state, "state")
);
}
window.addEventListener("scroll", this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScroll);
}
handleScroll(e) {
const scrollY = window.scrollY + window.innerHeight;
const { lockedIntoView, targetY } = this.state;
if (lockedIntoView) {
console.info("we locked");
// update the padding on the doc as we now removing the target from normal flow
window.document.body.style.paddingBottom = `${this.ref.getBoundingClientRect().height}px`;
// we passed the taret so reset - we have to factor the target height in the calc
if (scrollY > targetY + this.ref.getBoundingClientRect().height) {
window.document.body.style.paddingBottom = "0px";
this.setState({ lockedIntoView: false });
}
} else {
// if we scrollign down and at the target, then lock
if (
scrollY > lastScrollY &&
(scrollY >= targetY &&
scrollY < targetY + this.ref.getBoundingClientRect().height)
) {
console.info("we locked");
this.setState({ lockedIntoView: true });
}
}
// update last scroll position to determine if we going down or up
lastScrollY = scrollY;
}
render() {
const { lockedIntoView, targetY } = this.state;
const fixed = lockedIntoView ? "lockedIntoView" : "";
return (
<div className="App">
<div className="bg-near-white min-vh-100 ">
<h1>First section</h1>
</div>
<div
ref={r => (this.ref = r)}
className={`vh-100 bg-blue pa0 ma0 ${fixed}`}
>
<h2>
When this is in full view of the window, it should remain fixed
until the window has scrolled the full length of the window
</h2>
</div>
<div style={{ height: "300vh" }} className="bg-near-white min-vh-100 ">
<h2>The next section</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);