Next.js中Framer Motion移动端与桌面端初始动画适配问题求助
解决Next.js + Framer Motion 跨端动画初始状态错误问题
问题根源
你的自定义useMediaQuery初始状态设为false,而Next.js的SSR阶段无法访问window对象,无法获取真实的媒体查询结果;客户端组件挂载后,useEffect才会更新状态,但Framer Motion的初始动画在组件首次渲染时就已执行,导致用了错误的初始值触发动画。
解决方案
1. 修复useMediaQuery Hook,确保初始值正确
修改Hook,在客户端初始化时直接获取媒体查询结果,同时替换已废弃的addListener API:
import { useState, useEffect } from "react"; export default function useMediaQuery(query) { // 客户端初始化时直接获取匹配结果,SSR阶段返回默认值 const [matches, setMatches] = useState(() => { if (typeof window !== "undefined") { return window.matchMedia(query).matches; } // SSR时可根据需求设置默认值,比如默认移动端 return false; }); useEffect(() => { const media = window.matchMedia(query); const updateMatches = () => { setMatches(media.matches); }; // 监听媒体查询变化 media.addEventListener("change", updateMatches); // 确保初始化值准确 updateMatches(); return () => media.removeEventListener("change", updateMatches); }, [query]); return matches; }
2. 用动画控制器确保状态稳定后再启动动画
配合Framer Motion的useAnimation,等待useMediaQuery状态更新后再触发动画,避免初始值错误:
import { motion, useAnimation } from "framer-motion"; import useMediaQuery from "./useMediaQuery"; export default function WishCard() { // 修正变量名,避免逻辑混淆:isDesktop对应屏幕>=428px const isDesktop = useMediaQuery("(min-width: 428px)"); const controls = useAnimation(); // 状态稳定后启动动画 useEffect(() => { controls.start("visible"); }, [isDesktop, controls]); const wishCardVariant = { hidden: (isDesktop) => ({ opacity: 0, y: isDesktop ? 0 : "100%", // 移动端从下方切入 x: isDesktop ? "100%" : 0, // 桌面端从右侧切入 }), visible: { opacity: 1, x: 0, y: 0, transition: { duration: 0.5 }, }, }; return ( <motion.div variants={wishCardVariant} initial="hidden" animate={controls} custom={isDesktop} > {/* 卡片内容 */} </motion.div> ); }
3. 额外优化:修正变量名避免逻辑混淆
原代码中onMobile变量对应min-width:428px的匹配结果,逻辑上是“是否为桌面端”,改成isDesktop能避免后续维护时的逻辑混乱。
内容的提问来源于stack exchange,提问作者Alexandre PUJOL
相关产品推荐
相关产品推荐

