我正在尝试使用 Framer Motion 将页面过渡动画添加到 Next js 14 应用程序。我有 PageTransitionLayout.tsx 看起来像这样
'use client';
import { motion, AnimatePresence } from "framer-motion";
import { ReactNode, FC } from "react";
import { usePathname } from "next/navigation";
interface ILayoutProps {
children: ReactNode;
}
const PageTransitionLayout: FC<ILayoutProps> = ({ children }) => {
const pathname = usePathname()
return (
<AnimatePresence mode={'wait'}>
<motion.div
key={`${pathname}1`}
className="absolute top-0 left-0 w-full h-screen bg-green-400 origin-middle"
initial={{ scaleY: 1 }}
animate={{ scaleY: 0.5 }}
exit={{ scaleY: 0}}
transition={{ duration: 1, ease: [0.22, 1, 0.36, 1] }}
/>
{children}
</AnimatePresence>
);
}
export default PageTransitionLayout;
Run Code Online (Sandbox Code Playgroud)
并像这样在 app/contact/pages.tsx 中使用它
"use client";
import PageTransitionLayout from "../ui/PageTransitionLayout";
export default function Contact() {
return (
<PageTransitionLayout>
<div className="grid h-[90vh] place-items-center bg-orange-400">
<h1 className="font-bold text-4xl">Contact</h1>
</div>
</PageTransitionLayout>
)
}
Run Code Online (Sandbox Code Playgroud)
但是当导航到不同的页面时,运动 div 的退出动画不会触发。可能是什么问题?
小智 6
在 NextJS 13/14 中,您必须将页面包装在 HOC 中以减慢应用程序路由器的速度。下面提出的解决方案将引入更多需要解决的问题,例如:
loading.js应用程序目录中的文件的悬念边界将无法正确加载到子级中,从而使您无法使用loading.js
layout.js
/src/app/layout.js||/app/layout.js
import { Inter } from 'next/font/google'
import './globals.css'
import PageAnimatePresence from '@components/HOC/PageAnimatePresence'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'Your Website Title',
description: 'Website metadata description.',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body
className={inter.className + ` bg-blue-500 transition-colors duration-1000`}
id="page-container"
>
<PageAnimatePresence>{children}</PageAnimatePresence>
</body>
</html>
)
}
Run Code Online (Sandbox Code Playgroud)
PageAnimatePresence.js
/src/app/components/HOC/PageAnimatePresence.js||/app/components/HOC/PageAnimatePresence.js
'use client'
import { usePathname } from 'next/navigation'
import { AnimatePresence, motion } from 'framer-motion'
import FrozenRoute from './FrozenRoute'
const PageAnimatePresence = ({ children }) => {
const pathname = usePathname()
return (
<AnimatePresence mode="wait">
{/**
* We use `motion.div` as the first child of `<AnimatePresence />` Component so we can specify page animations at the page level.
* The `motion.div` Component gets re-evaluated when the `key` prop updates, triggering the animation's lifecycles.
* During this re-evaluation, the `<FrozenRoute />` Component also gets updated with the new route components.
*/}
<motion.div key={pathname}>
<FrozenRoute>{children}</FrozenRoute>
</motion.div>
</AnimatePresence>
)
}
export default PageAnimatePresence
Run Code Online (Sandbox Code Playgroud)
FrozenRoute.js
/src/app/components/HOC/FrozenRoute.js||/app/components/HOC/FrozenRoute.js
'use client'
import { useContext, useRef } from 'react'
import { LayoutRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime'
const FrozenRoute = ({ children }) => {
const context = useContext(LayoutRouterContext)
const frozen = useRef(context).current
return <LayoutRouterContext.Provider value={frozen}>{children}</LayoutRouterContext.Provider>
}
export default FrozenRoute
Run Code Online (Sandbox Code Playgroud)
template.js
/src/app/template.js||/app/template.js
'use client'
import { motion } from 'framer-motion'
const variants = {
hidden: { opacity: 0, x: 0, y: 0 },
enter: { opacity: 1, x: 0, y: 0 },
}
export default function Template({ children }) {
return (
<motion.main
variants={variants}
initial="hidden"
exit="hidden"
animate="enter"
transition={{ type: 'linear', duration: 0.25 }}
key="LandingPage"
>
{children}
</motion.main>
)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
836 次 |
| 最近记录: |