使用React和styled-components的页面滚动进度条
让我们重新实现您可能在包括这个博客在内的许多流行博客上看到的页面滚动效果的进度条。
我们将如何做
为了实现这一点,我们将使用 React、styled-components 和 Document onScroll 事件。 所以,事不宜迟,让我们开始吧。
安装
如前所述,我们将使用 React
和 styled-components
来实现进度滚动效果。 为了节省时间,我们将使用 Create React App 为我们引导一个快速的 React 应用程序。
所以打开你的终端并运行以下命令:
$ create-react-app app-name $ cd app-name
没有安装`create-react-app`? 然后你可以使用这个命令$ npx create-react-app app-name
好的,接下来我们还需要将 styled-components
安装到我们新创建的项目中。 所以在项目目录中,运行
$ npm install styled-components -S
现在,如果您在我们的终端中运行 npm start
,您应该会看到如下内容:
设计布局
所以,现在我们的应用程序已经设置好了,让我们开始编写一些实际的代码。 我们将从构建页面布局开始,并编写一些 CSS 来设置布局样式。
导航到您的 src/App.js
文件,然后删除文件中的所有内容并添加以下代码行:
src/App.js
import React, { Component } from 'react'; import './App.css'; export default class App extends Component { render() { return ( <div className="App"> <header></header> <main> <h1>Lorem Ipsum</h1> <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> <p>...more paragraphs of text, enough so that the page gets a scrollbar</p> </main> </div> ); } }
基本上我们刚刚所做的就是将 create-react-app
为我们生成的无状态功能组件转换为基于类的组件。 我们还添加了一些虚拟文本内容来填充页面。
现在很酷,为布局添加一些简单的样式。 打开 src/App.css
并删除其中的所有内容,然后添加以下 CSS 行。
header { background: linear-gradient(to right, #302E24, #28313E); height: 60px; width: 100%; box-shadow: 0 2px 20px 0 rgba(0, 0, 0, .1); margin-bottom: 70px; } .App p, .App h1 { margin: 30px auto; width: 800px; }
现在运行 npm start
,您应该会看到类似下面的内容:
设计进度条
好的,接下来我们议程上的事情就是设计进度条。 为了实现这一点,我们将使用我们安装的 styled-components
库。
让我们在我们的 src 文件夹中创建一个名为 Progress.js
的文件。 完成后,将以下代码行添加到文件中:
src/Progress.js
import styled from 'styled-components'; const Progress = styled.div` position: fixed; background: linear-gradient( to right, rgba(250, 224, 66, .8) ${props => props.scroll}, transparent 0); width: 100%; height: 4px; z-index: 3; `; export default Progress;
现在,让我们回到我们的 App.js
文件并使用新创建的 Progress
组件:
src/App.js
import React, { Component } from 'react'; import './App.css'; import Progress from './Progress'; export default class App extends Component { render() { return ( <div className="App"> <Progress scroll="70%" /> {/* ... */} </div> ); } }
好吧!,我想现在是检查我们目前做得如何的好时机。 所以让我们再次运行 npm start
并检查我们的应用程序。 如果一切顺利,你应该有这样的东西:
添加滚动逻辑
有趣的部分来了,我们实现了滚动逻辑。 在我们开始编写实际代码之前,让我们概述实现此目标所需的步骤:
- 监听
document
对象上的滚动事件 - 判断用户当前的滚动距离
- 有一个保存当前滚动百分比的状态变量
- 最后,用新的状态百分比更新进度滚动
好吧,让我们将伪代码转换为实际代码
监听文档对象的滚动事件
好的,所以在我们的 App.js
文件中,让我们创建一个名为 listenToScrollEvent
的方法,它将监听 DOM 上的滚动事件。 这个方法看起来有点像这样:
src/App.js
listenToScrollEvent = () => { document.addEventListener("scroll", () => { requestAnimationFrame(() => { // Calculates the scroll distance this.calculateScrollDistance(); }); }); };
该方法只是侦听 Document
对象上发生的任何滚动事件,然后调用 requestAnimationFrame,它通知浏览器动画即将发生。 然后浏览器调用 requestAnimationFrame
中指定的 calculateScrollDistance
方法。
我们需要做的最后一步是在 App
组件挂载时调用此方法。 所以我们利用 React 的生命周期方法之一 componentDidMount
来调用 listenToScrollEvent
方法。
src/App.js
componentDidMount() { this.listenToScrollEvent(); }
现在让我们来定义 calculateScrollDistance
方法。
判断用户当前滚动距离
要计算用户当前的滚动距离,我们需要做几件事:
- 我们需要获取用户滚动了多少
- 我们需要获取浏览器的高度
- 我们需要获取文档的高度
获取用户滚动了多少
要计算当前用户的滚动距离,我们可以利用 windows
对象上可用的 pageYOffset
值。
因此,让我们创建一个 calculateScrollDistance
方法并将以下行添加到方法中:
src/App.js
calculateScrollDistance = () => { const scrollTop = window.pageYOffset; }
对于较旧的 IE 浏览器,以下内容也应该可以工作 document.body.scrollTop
获取浏览器的高度
接下来我们需要获取浏览器的高度,为此,我们可以使用可以通过 window
对象访问的 innerHeight
值。
浏览器的高度只是指可查看的浏览器(即 Chrome 或 Firefox)区域的高度。
所以回到我们的 calculateScrollDistance
,让我们添加获取浏览器窗口高度的行。
src/App.js
calculateScrollDistance = () => { const scrollTop = window.pageYOffset; const windowHeight = window.innerHeight; }
对于较旧的 IE 浏览器,以下内容也应该可以工作 document.body.clientHeight
获取文档高度
现在获取文档的高度很棘手,这背后的原因是因为各种浏览器有不同的方式来解释或计算文档的高度。
为了绕过这个,我们需要检查各种浏览器用来获取文档高度的不同属性,并利用 Math.max()
来获取最高值。
因此,让我们通过创建一个名为 getDocHeight
的方法来快速实现这一点,并添加以下代码行:
src/App.js
getDocHeight = () => { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight ); }
然后我们在 calculateScrollDistance
方法中调用该方法,如下所示:
src/App.js
calculateScrollDistance = () => { const scrollTop = window.pageYOffset; const windowHeight = window.innerHeight; const docHeight = this.getDocHeight(); }
现在我们有了所需的所有值,我们可以通过将 scrollTop
除以文档的总可用滚动长度(docHeight - winHeight)并将结果乘以 100 来计算用户滚动的百分比结果以百分比表示。
因此,将以下行添加到我们的代码中:
src/App.js
calculateScrollDistance = () => { const scrollTop = window.pageYOffset; const windowHeight = window.innerHeight; const docHeight = this.getDocHeight(); const totalDocScrollLength = docHeight - winHeight; const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100) }
现在我们有了滚动位置,我们需要添加一个 state
变量,然后我们可以使用用户的当前滚动位置进行更新。 为此,我们需要在您的 src/App.js
文件中创建一个状态对象并添加一个 scrollPosition
,然后将初始状态设置为 0。
src/App.js
// ... state = { scrollPosition: 0 }
回到我们的 calculateScrollDistance
方法,然后我们需要使用 React 提供的 setState
方法,这将有助于更新 scrollPosition
的状态:
src/App.js
calculateScrollDistance = () => { // ... this.setState({ scrollPosition, }); }
所以最终的代码看起来像这样:-
src/App.js
// ... state = { scrollPosition: 0 } calculateScrollDistance = () => { const scrollTop = window.pageYOffset; const winHeight = window.innerHeight; const docHeight = this.getDocHeight(); const totalDocScrollLength = docHeight - winHeight; const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100); this.setState({ scrollPostion, }); } // ...
使用我们的 scrollPosition 状态更新进度条
最后要做的就是简单地将我们的 scrollPosition
状态传递给我们的 Progress
条形组件。
{/* ... */} <Progress scroll={ this.state.scrollPostion + '%' } />
所以最终的完整代码看起来像这样:-
import React, { Component } from 'react'; import Progress from './Progress'; import './App.css'; export default class App extends Component { state = { scrollPostion: 0 } listenToScrollEvent = () => { document.addEventListener("scroll", () => { requestAnimationFrame(() => { this.calculateScrollDistance(); }); }); } calculateScrollDistance = () => { const scrollTop = window.pageYOffset; // how much the user has scrolled by const winHeight = window.innerHeight; const docHeight = this.getDocHeight(); const totalDocScrollLength = docHeight - winHeight; const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100) this.setState({ scrollPostion, }); } getDocHeight = () => { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight ); } componentDidMount() { this.listenToScrollEvent(); } render() { return ( <div className="App"> <Progress scroll={this.state.scrollPostion + '%'} /> <header></header> <main> <h1>Lorem Ipsum</h1> <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> <p> ... </p> </main> </div> ); } }
有了所有这些,您现在应该在页面滚动上有一个工作进度条!
这就是所有人
哇! 🤗 伙计们,我们成功创建了进度滚动效果。 希望你觉得它有用!