使用 React 和 Framer Motion 实现平滑滚动
使用带凹槽的鼠标滚轮滚动浏览网页时,页面通常会跳动,难以操作。
平滑滚动或弹簧滚动为传统的鼠标滚动增添了动画效果。
如果您从未体验过流畅滚动,请尝试在线演示。
概念
考虑一下视口窗口。当一堆 HTML 元素组合起来的高度超过窗口高度时,就会发生溢出,向下滚动时就可以访问到这些溢出的内容。
要覆盖默认滚动条,我们需要将内容包裹在一个我们可以控制的固定元素中。
最后,我们将创建一个不可见的间隔元素(div),其高度等于内容的滚动高度。这将触发浏览器的默认滚动条。
设置
在Replit上创建一个React Typescript Repl即可开始。
安装Framer Motion。npm install framer-motion
组件
该<SmoothScroll/>组件将包裹所有我们想要融入平滑滚动效果的 HTML 元素。
export default function SmoothScroll({
children
}: {
children: React.ReactNode;
}) {
return <></>;
}
滚动和弹簧值
从以下位置导入钩子framer-motion。
import { useScroll, useSpring, useTransform } from 'framer-motion';
在组件中,从钩子中SmoothScroll解构。scrollYProgressuseScroll
const { scrollYProgress } = useScroll();
接下来,使用useSpring钩子函数将平滑效果应用于该scrollYProgress值。
const smoothProgress = useSpring(scrollYProgress, { mass: 0.1 })
内容和间隔
将该组件添加motion到现有的导入中framer-motion。
- import { useScroll, useSpring } from 'framer-motion';
+ import { motion, useScroll, useSpring } from 'framer-motion';
跳至组件的return语句部分。返回一个空<div>元素和一个将该属性<motion.div>渲染children为子元素的元素。
return <>
<div style={{ height: contentHeight }} />
<motion.div
className="scrollBody"
>
{children}
</motion.div>
</>
通过hook 和state创建一个contentRefReact 引用。useRefcontentHeightuseState
import { useState, useRef } from 'react';
...
const contentRef = useRef<HTMLDivElement>(null);
const [contentHeight, setContentHeight] = useState(0);
将内容包装器分配给contentRef.
<motion.div
className="scrollBody"
ref={contentRef}
>
{children}
</motion.div>
使用style道具使垫片的高度为contentHeight。
<div style={{ height: contentHeight }} />
调整大小处理程序
当窗口大小改变时,内容的高度很可能会发生变化。由于该contentHeight值是一个状态,因此我们需要在每次窗口大小改变以及contentRef引用更新时更新它。
useEffect首先从以下位置导入钩子react:
- import { useState, useRef } from 'react';
+ import { useEffect, useState, useRef } from 'react';
在 useEffect 钩子中,创建并调用一个处理程序来设置contentHeight值contentRef.current.scrollHeight。
添加contentRef到依赖项数组中。
useEffect(() => {
const handleResize = () => {
if (contentRef.current) {
setContentHeight(contentRef.current.scrollHeight)
}
}
handleResize();
}, [contentRef]);
最后,在 useEffect 钩子中添加resize事件监听器,并返回一个处置函数。window
useEffect(() => {
...
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
}
}, [contentRef, children]);
把所有内容整合起来
useTransform从以下位置导入钩子framer-motion:
- import { motion, useScroll, useSpring } from 'framer-motion';
+ import { useTransform, motion, useScroll, useSpring } from 'framer-motion';
创建一个常量y,并将其设置为useTransform钩子的smoothProgress初始值。
const y = useTransform(smoothProgress, value => {
return value;
});
为了可视化如何计算滚动,我们将从内容容器的滚动高度中减去视口高度,然后乘以 -1,最后乘以。value
const y = useTransform(smoothProgress, value => {
return value * -(contentHeight - window.innerHeight);
});
y在内容容器中使用转换后的值。
<motion.div
className="scrollBody"
style={{ y }}
ref={contentRef}
>
{children}
</motion.div>
款式
我已经帮你搞定了 CSS,所以你不用再费心了。你只需要复制粘贴最基本的 CSS 代码,滚动组件就能正常运行了。
.scrollBody {
width: 100vw;
position: fixed;
top: 0;
display: flex;
flex-direction: column;
}
完成🎉
就是这样!你只需要在组件中添加一些HTML元素即可<SmoothScroll />。
<h1>Lorem Ipsum</h1>代码库中没有什么比五十个 s 更好了。
- 源代码:https://replit.com/@IroncladDev/FramerSmoothScroll?v =1
- 在线演示:https://framersmoothscroll.ironcladdev.repl.co
感谢阅读。如果您对本文有任何反馈或建议,欢迎在评论区留言。
让我们联系吧🤝
文章来源:https://dev.to/ironcladdev/smooth-scrolling-with-react-framer-motion-dih



