如何实现滚动时基于视口底部20%区域切换body背景色?
解决方案:基于滚动事件的精准背景色切换
直接监听滚动事件并计算目标位置的方式,比IntersectionObserver更易控制逻辑,能精准匹配视口底部20%的区域对应的区块。以下是具体实现:
核心思路
- 计算视口底部20%位置的绝对坐标:
滚动距离 + 视口高度 × 0.8 - 遍历所有区块,找到该坐标落在的目标区块
- 提取目标区块的
data-background值,设置为body的背景色 - 优化性能:用
useRef缓存区块DOM引用,避免重复查询;用状态避免重复设置背景色
React 代码实现
import { useEffect, useRef, useState } from 'react'; const ScrollBgSwitcher = () => { // 缓存所有区块的DOM引用 const blockRefs = useRef([]); // 记录当前激活的背景色,避免重复DOM操作 const [activeBg, setActiveBg] = useState(''); useEffect(() => { const updateBodyBg = () => { // 计算视口底部20%的绝对位置 const targetY = window.scrollY + window.innerHeight * 0.8; // 从下往上遍历区块,优先匹配最下方的目标区块 for (let i = blockRefs.current.length - 1; i >= 0; i--) { const block = blockRefs.current[i]; if (!block) continue; const rect = block.getBoundingClientRect(); const blockTop = rect.top + window.scrollY; const blockBottom = rect.bottom + window.scrollY; // 检查目标点是否落在当前区块内 if (targetY >= blockTop && targetY <= blockBottom) { const newBg = block.dataset.background; if (newBg !== activeBg) { document.body.style.backgroundColor = newBg; setActiveBg(newBg); } break; } } }; // 页面加载时初始化背景色 updateBodyBg(); // 绑定滚动事件,passive优化性能 window.addEventListener('scroll', updateBodyBg, { passive: true }); // 组件卸载时清理事件监听 return () => { window.removeEventListener('scroll', updateBodyBg); }; }, [activeBg]); // 生成10个带不同背景色的区块 const colorList = [ '#ff4444', '#00C851', '#33b5e5', '#ffbb33', '#aa66cc', '#ff8800', '#0099cc', '#99cc00', '#ff4444', '#33b5e5' ]; const blocks = colorList.map((color, idx) => ( <section key={idx} ref={el => el && (blockRefs.current[idx] = el)} data-background={color} style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: '2.5rem', fontWeight: 'bold' }} > 区块 {idx + 1} </section> )); return <div>{blocks}</div>; }; export default ScrollBgSwitcher;
关键细节说明
- 从下往上遍历:当滚动时,可能多个区块与目标位置有重叠,从下往上找第一个匹配的区块,能确保是当前视口底部20%对应的正确区块
- passive事件监听:
{ passive: true }告诉浏览器滚动事件不会调用preventDefault(),提升滚动流畅度 - 缓存DOM引用:
useRef保存区块DOM,避免每次滚动都通过querySelector查询,减少性能开销 - 避免重复更新:用
activeBg状态记录当前背景色,只有当颜色变化时才更新body样式,减少不必要的DOM操作
关于IntersectionObserver的补充说明
如果坚持用IntersectionObserver,需要调整根边距来匹配视口底部20%区域,配置如下:
const observerConfig = { root: null, // 把视口上80%区域排除,只监听底部20% rootMargin: `0px 0px ${-window.innerHeight * 0.8}px 0px`, threshold: 0.1 // 区块有10%进入目标区域时触发 };
但这种方式需要处理多个区块同时交叉的优先级问题,逻辑复杂度更高,不如直接计算坐标的方式直观可控。
内容的提问来源于stack exchange,提问作者Donnie Berry
相关产品推荐
相关产品推荐

