如何在用 AnimatePresence 包装的 Next.js 页面中导航和滚动到带有 ID 的元素

iam*_*lli 6 javascript scroll reactjs next.js framer-motion

我正在使用Framer MotionNext.js页面过渡设置动画。但是,使用AnimatePresence会破坏hash链接导航,页面不再转到目标id元素。

页面转换是完美的,直到您想导航到页面上的苛刻 ID :(

// I have a link component setup like this
// index.tsx
<Link href="/about#the-team" scroll={false}>
  <a>The Team</a>
</Link>

// Targeting another page `about.tsx` with the id
// about.tsx

{/* ...many sections before.. */}
<section id="the-team">{content}</section>
Run Code Online (Sandbox Code Playgroud)

我有一个自定义_app.tsx,如下所示。

// _app.tsx
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { AnimatePresence } from 'framer-motion';

const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => {
  const router = useRouter();
  return (
    <AnimatePresence exitBeforeEnter>
      <Component {...pageProps} key={router.route} />
    </AnimatePresence>
  );
};

export default MyApp;

Run Code Online (Sandbox Code Playgroud)

我希望直接转到该部分,id="the-team"但它不起作用。带有哈希链接的页面刷新显示它最初位于目标元素但很快跳到顶部。它是如此之快,而且很容易错过。如何保留页面转换但仍然能够导航到哈希 ID?

iam*_*lli 5

罪魁祸首是 上的exitBeforeEnter 道具AnimatePresence。删除道具修复了哈希 id 导航,但破坏了我的一些用例。

如果设置为 true,AnimatePresence则一次仅渲染一个组件。退出组件将在渲染进入组件之前完成其退出动画。-成帧运动文档

我不能只是删除exitBeforeEnter道具,因为我已经包含它来修复一个错误,我的目标是进入页面中的一个节点与退出页面的旧实例中的相同节点相冲突。例如ref,退出页面中动画 svg 标题上的逻辑与进入页面的标题 svg 引用逻辑发生冲突。

为了两全其美,使用onExitComplete “当所有退出节点都完成动画输出时触发”,我向它传递了一个回调,该回调检查从widow.location.hash和平滑滚动到 id的哈希使用scrollIntoView 注意:onExitComplete仅当exitBeforeEnterprop 是有效的true.

// pages/_app.tsx
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { AnimatePresence } from 'framer-motion';

// The handler to smoothly scroll the element into view
const handExitComplete = (): void => {
  if (typeof window !== 'undefined') {
    // Get the hash from the url
    const hashId = window.location.hash;

    if (hashId) {
      // Use the hash to find the first element with that id
      const element = document.querySelector(hashId);

      if (element) {
        // Smooth scroll to that elment
        element.scrollIntoView({
          behavior: 'smooth',
          block: 'start',
          inline: 'nearest',
        });
      }
    }
  }
};

const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => {
  const router = useRouter();
  return (
    <AnimatePresence exitBeforeEnter onExitComplete={handExitComplete}>
      <Component {...pageProps} key={router.route} />
    </AnimatePresence>
  );
};

export default MyApp;

Run Code Online (Sandbox Code Playgroud)

此处为Live CodeSandbox

PS:出于某种原因,window.location.hash沙箱中的预览总是一个空字符串,打破了哈希导航,但在单独的浏览器选项卡中打开预览就像一个魅力。

  • 非常感谢您,在无限滚动目录(存储在上下文中)上遇到了确切的问题;这正是我所需要的。 (2认同)