React中如何延迟滚轮事件监听,避免过渡动画冲突?
React滚轮路由导航动画冲突问题解决方案
我正在开发一款React应用,通过鼠标滚轮上下滚动实现路由导航,并配合对应方向的framer-motion动画:向下滚动时,下一路由从下方进入,当前路由从下方退出;向上滚动则相反。
现有实现代码
自定义Hook useAppDirection.js
import { useState, useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom" function useAppDirection(navDown, navUp, directionValueDown, directionValueUp) { const navigate = useNavigate(); const location = useLocation(); const { pathname } = location; const [isUp, setIsUp] = useState(false); useEffect(() => { function handleNavigation(e) { if (e.deltaY > 1) { setIsUp(false) setTimeout(() => { navigate(navDown, { state: { value: directionValueDown } }); }, 200) } else if (e.deltaY < 1) { setIsUp(true) setTimeout(() => { navigate(navUp, { state: { value: directionValueUp } }); }, 200) } } window.addEventListener("wheel", handleNavigation); return () => window.removeEventListener("wheel", handleNavigation); }, [pathname]); return { location, isUp } } export default useAppDirection
组件 AboutMe.js
import React from "react"; import { motion } from "framer-motion"; import useAppDirection from "../Hooks/useAppDirection"; export default function AboutMe() { const { isUp, location } = useAppDirection("/work", "/skills", 1000, -1000); return ( <motion.div className="aboutme--top" initial={{ opacity: 0, y: location.state.value, transition: { duration: 0.8 } }} animate={{ opacity: 1, y: 0, transition: { duration: 0.8 } }} exit={{ opacity: 0, y: isUp ? 1300 : -1300, transition: { duration: 0.8 } }} > </motion.div > ) }
遇到的问题
当用户向一个方向滚动触发过渡后,中途反向滚动,两个动画会同时执行,导致过渡效果卡顿、混乱。需要修改代码,确保只有当前过渡完成(约0.8秒)后,才能触发下一次滚动事件——过渡期间禁用滚轮事件监听,完成后重新启用。
解决方案
修改useAppDirection.js,添加状态控制滚轮事件的触发权限,在过渡期间禁用、完成后恢复:
import { useState, useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom" function useAppDirection(navDown, navUp, directionValueDown, directionValueUp) { const navigate = useNavigate(); const location = useLocation(); const { pathname } = location; const [isUp, setIsUp] = useState(false); // 标记当前是否处于过渡动画中 const [isTransitioning, setIsTransitioning] = useState(false); // 和组件动画时长保持一致,单位ms const TRANSITION_DURATION = 800; useEffect(() => { function handleNavigation(e) { // 过渡中直接忽略滚轮事件 if (isTransitioning) return; if (e.deltaY > 1) { setIsUp(false); setIsTransitioning(true); setTimeout(() => { navigate(navDown, { state: { value: directionValueDown } }); // 动画结束后重新启用滚轮 setTimeout(() => setIsTransitioning(false), TRANSITION_DURATION); }, 200); } else if (e.deltaY < -1) { // 修正判断逻辑,仅当明显向上滚动时触发 setIsUp(true); setIsTransitioning(true); setTimeout(() => { navigate(navUp, { state: { value: directionValueUp } }); setTimeout(() => setIsTransitioning(false), TRANSITION_DURATION); }, 200); } } window.addEventListener("wheel", handleNavigation); return () => window.removeEventListener("wheel", handleNavigation); }, [pathname, isTransitioning, navigate, navDown, navUp, directionValueDown, directionValueUp]); return { location, isUp } } export default useAppDirection
关键修改点
- 新增
isTransitioning状态,用于锁定过渡期间的滚轮事件 - 在
handleNavigation开头判断状态,过渡中直接返回不处理 - 触发导航时设置
isTransitioning为true,并在动画时长后重置为false,恢复滚轮监听 - 修正向上滚动的判断逻辑:原代码
e.deltaY < 1会包含无滚动的情况,改为e.deltaY < -1更精准
内容的提问来源于stack exchange,提问作者jacksonmendoza
相关产品推荐
相关产品推荐

