面试题答案
一键面试整体实现方案
- 获取滚动事件:使用
window.addEventListener('scroll', callback)
监听滚动事件,在回调函数中获取当前滚动位置。 - 计算滚动速度:记录上一次滚动位置,通过与当前滚动位置的差值和时间间隔来计算滚动速度。
- 使用
requestAnimationFrame
:利用requestAnimationFrame
来实现动画,该方法会在浏览器下一次重绘之前调用传入的回调函数,确保动画流畅。 - 性能优化:
- 节流:为了避免滚动事件过于频繁触发,导致性能问题,可以使用节流函数,限制回调函数在一定时间内只执行一次。
- 取消监听:在组件卸载时,取消滚动事件的监听,防止内存泄漏。
- 帧率自适应:根据设备的刷新率动态调整动画速度,确保在不同帧率设备上都能流畅运行。
核心代码逻辑
import React, { useEffect, useRef } from 'react';
const AnimatedComponent = () => {
const animationFrameRef = useRef(null);
const previousScrollTopRef = useRef(0);
const previousTimeRef = useRef(null);
const handleScroll = () => {
const currentScrollTop = window.pageYOffset;
const currentTime = performance.now();
if (previousTimeRef.current) {
const scrollSpeed = (currentScrollTop - previousScrollTopRef.current) / (currentTime - previousTimeRef.current);
// 根据滚动速度调整动画状态,这里简单以log形式展示
console.log(`Scroll speed: ${scrollSpeed}`);
}
previousScrollTopRef.current = currentScrollTop;
previousTimeRef.current = currentTime;
};
const animate = () => {
// 这里进行具体的动画操作,例如改变组件的样式
// 这里简单以log形式展示
console.log('Animation frame');
animationFrameRef.current = requestAnimationFrame(animate);
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
animationFrameRef.current = requestAnimationFrame(animate);
return () => {
window.removeEventListener('scroll', handleScroll);
cancelAnimationFrame(animationFrameRef.current);
};
}, []);
return (
<div>
{/* 组件内容 */}
</div>
);
};
export default AnimatedComponent;
以上代码展示了如何在React组件中监听滚动事件并结合requestAnimationFrame
实现动画。handleScroll
函数负责计算滚动速度,animate
函数负责执行动画帧,useEffect
钩子用于添加和移除滚动事件监听以及启动和取消动画。