加载中...
爱酷文化
爱酷文化
  • 首页
  • 关于
    • 关于我们
    • 团队
  • 服务
  • 案例
    • 电子商务
    • 视觉传达
  • 解决方案
  • 博客
    • 新闻
    • 众筹
    • Shopify
    • 技术
使用 Framer Motion 实现响应式动画
  • 首页
  • 技术博客
  • 使用 Framer Motion 实现响应式动画

使用 Framer Motion 实现响应式动画

  • ADMIN
  • 2024 - 5 - 2
  • 标签:  
  • Framer motion
  • React
  • Animation
  • Media Query
  • Javascript
  • Typescript
爱酷文化

独立站,Shopify,电商平台搭建,前端开发,建站,外贸,亚马逊,Amazon,视觉设计,系统开发,电商架构,uni,app

爱酷文化

Ceo

Contact us

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Send
background
爱酷文化

独立站,Shopify,电商平台搭建,前端开发,建站,外贸,亚马逊,Amazon,视觉设计,系统开发,电商架构,uni,app

  • 站点地图

© 爱酷文化 2024.

All Rights Reserved.

Framer Motion 可以非常轻松地创建出美观的动画,但如果你想为不同的屏幕尺寸设置不同的动画效果该怎么办?使用 CSS 动画,你可以直接使用媒体查询,但你知道吗,通过利用 Window.matchMedia API 或使用 CSS 变量,我们可以为 Framer Motion 和 React Spring 等 JavaScript 动画库编写响应式动画?

在 JavaScript 中使用媒体查询

在 React 中使用自定义 hook 和 window.matchMedia API 是在 Framer Motion 中制作响应式动画的最简单方法。许多 UI 框架(例如 Material UI 和 Chakra UI)已经公开了这样的 hook,但如果你想自己编写一个,可以参考以下内容:

export function useMediaQuery(query) {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const media = window.matchMedia(query);
    if (media.matches !== matches) {
      setMatches(media.matches);
    }

    const listener = () => {
      setMatches(media.matches);
    };

    if (typeof media.addEventListener === "function") {
      media.addEventListener("change", listener);
    } else {
      media.addListener(listener);
    }

    return () => {
      if (typeof media.removeEventListener === "function") {
        media.removeEventListener("change", listener);
      } else {
        media.removeListener(listenerList);
      }
    };
  }, [matches, query]);

  return matches;
}

并非所有媒体查询 hook 都能与服务器端渲染配合使用,而只在客户端更新值。如果你觉得这一点很重要,请考虑使用下面描述的 CSS 变量选项。

它获取一个媒体查询字符串(就像你在 css 中编写的那样),如果查询与当前屏幕匹配,则返回 true。如果屏幕大小调整,则该值将更新。在代码中像这样使用它

    const isSmall = useMediaQuery("(min-width: 480px)");

一个好主意是设置自定义钩子,使其与您已经在 css 中使用的媒体查询相匹配,这样您就不必记住像素值(是 479 还是 480?):

export const useIsSmall = () => useMediaQuery('(min-width: 480px)');
export const useIsMedium = () => useMediaQuery('(min-width: 768px)');

/* etc.. */

通过Variants实现响应式动画

现在我们已经设置好了钩子,让我们通过使用变体来根据媒体查询有条件地进行更改,从而将所有内容组合在一起。

import { motion } from 'framer-motion'
import { useIsSmall } from 'src/hooks/utils'

const Component = () => {
    const isSmall = useIsSmall() /* or useMediaQuery('(min-width: 480px)'); */

    const variants = isSmall
    ? {
        animate: {
          opacity: 1,
          scale: 1,
                y: 0,
        },
        exit: {
          opacity: 1,
          scale: 1,
                y: 500,
        },
      }
    : {
        animate: {
          opacity: 1,
          scale: 1,
          y: 0,
        },
        exit: {
          opacity: 0,
          scale: 0.9,
          y: -10,
        },
      };

    return (
        <motion.div initial="exit" animate="animate" exit="exit">Animated</motion.div>
    );
}

你也可以内联使用变量<motion.div animate={isSmall ? { y: 500} : { y: 1000}} />,但我发现大多数情况下,将其与变体一起使用是最简洁的方法。

使用 CSS Variants 实现响应式 Framer Motion

使用 Framer Motion 制作响应式动画的另一种方法是使用 CSS 变量,正如 Sam Selikoff 在 这个精彩的视频 中使用 Tailwind CSS 所展示的那样。这比使用媒体查询钩子稍微复杂一些,但具有与服务器端渲染最佳配合的优势。

诀窍在于将 CSS 变量用作 varient 的值,然后使用媒体查询在断点处更新变量。以下是前面示例的重构:

const Component = () => {
    const variants = {
        animate: {
            opacity: 'var("--opacity-animate")',
            scale: 'var("--scale-animate")',
            y: 'var("--y-animate")'
        },
        exit: {
            opacity: 'var("--opacity-exit")',
            scale: 'var("--scale-exit")',
            y: 'var("--y-exit")',
        },
    }

    return (
        <motion.div
            initial="exit"
            animate="animate"
            exit="exit"
            className="component"
        >
            Animated
        </motion.div>
    );
}

然后在你的 CSS 中使用媒体查询覆盖变量。请注意,你只需要指定在断点处更改的值,其余的值将从初始值继承。

.component {
  --opacity-animate: 1;
  --scale-animate: 1;
  --y-animate: "0px";
  --opacity-exit: 0;
  --scale-exit: 0.9;
  --y-exit: "-10px";

  @media (min-width: 480px) {
    --opacity-exit: 1;
    --scale-exit: 1;
    --y-exit: "500px";
  }
}