news 2026/9/10 20:01:24

使用 Framer Motion 为 React 应用打造文本与图片动画:Refine 项目实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Framer Motion 为 React 应用打造文本与图片动画:Refine 项目实战指南

使用 Framer Motion 为 React 应用打造文本与图片动画:Refine 项目实战指南

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

本文以 Refine 仓库官方博客中的 Framer Motion 教程为主体,系统讲解如何在 React 应用中用极少量代码实现文本入场动画、悬停反馈以及图片旋转轮播、拖拽与点按缩放等交互动画,并对照 Refine 文档站源码(documentation/package.json 与 documentation/src/refine-theme 等)展示该库在真实项目中的落地方式。读完本文,你将掌握motion组件、animate/initial/transition/whileHover/whileTap/drag等核心 props 的用法,并能直接复制代码到自己的 React 项目中运行。

Framer Motion 是什么

Framer Motion 是 Framer 团队推出的、可投入生产环境的 React 动画库,它能够直接在 DOM 元素上创建多种动画效果,是编写复杂 CSS 动画的优秀替代方案。相比手写大量 CSS keyframes,Framer Motion 把动画声明收敛为组件上的 props,让开发者把精力集中在业务本身。

在使用它之前,需要在 React 项目中安装依赖并导入所需组件。

环境准备

跟随本文动手实践前,需要具备:

  • 对 HTML、CSS、JavaScript 基础前端技术有清晰理解;
  • 了解 React.js 基础知识;
  • 本机已安装 Node.js。

React 项目中的外部库统一通过包管理器安装,执行:

npm install framer-motion

安装完成后,在需要使用动画的组件中导入 Framer Motion 的组件即可开始编码。本文内容分为两部分:先完成文本动画,再完成图片动画,最后两者共同组成一个完整的 React 应用。

文本动画:用 motion 组件让标题动起来

认识 motion 组件

App.js中加入如下代码:

import { motion } from "framer-motion"; function App() { return ( <div className="App"> <motion.h1 animate={{ x: [50, 150, 50], opacity: 1, scale: 1 }} transition={{ duration: 5, delay: 0.3, ease: [0.5, 0.71, 1, 1.5], }} initial={{ opacity: 0, scale: 0.5 }} whileHover={{ scale: 1.2 }} > Animation made easy with Framer Motion </motion.h1> </div> ); } export default App;

注意这里的h1与标准h1元素的差别:它使用了motion前缀。motion组件是使用 Framer Motion 制作动画的第一步,它可以配合任意标准 HTML 元素使用(motion.divmotion.h1motion.button等),其本质是把 HTML 元素封装成 React 组件,再通过 props 驱动动画。

initial 与 animate:声明动画的起点与终点

为了让文本在页面加载时拥有一个渐显的入场动画,我们同时使用了initialanimate两个 props:

  • initial定义元素的初始状态,这里设置为{ opacity: 0, scale: 0.5 },即一开始文字透明且缩小到一半;
  • animate定义元素的动画目标状态,这里设置为{ opacity: 1, scale: 1 },即最终完全显示并恢复原始大小。

Framer Motion 会自动计算并补间这两个状态之间的过渡。此外,animate还支持传入关键帧数组,让元素按顺序经过多个值。代码中的x: [50, 150, 50]表示文本沿 x 轴从 50 移动到 150,再回到 50(原文中给出的写法为x: [0, 150, 50],效果等价,均表示一个往返移动)。opacity取 1、scale取 1,当缩放达到 1 时文本停止增大尺寸,避免“狂野”的视觉效果。

transition:控制动画节奏

每一个有效的动画都离不开过渡过程。transitionprop 用来描述动画如何播放,包括三个关键参数:

  • duration:动画持续时长(秒),示例中为 5;
  • delay:动画开始前的延迟时长(秒),示例代码中为 0.3(原文叙述中写作 3,以代码 0.3 为准可让入场衔接更自然);
  • ease:缓动函数。示例使用贝塞尔曲线数组[0.5, 0.71, 1, 1.5](原文叙述中写作[0.5, 0.71, 1, 1.01]),它描述了文本动画在进入原始缩放值之前以怎样的速率进行,改变数组即可获得不同的“弹入”节奏。

参数一致性提示:原文档正文对delayease数值的描述和代码示例略有出入,实际运行时以组件代码中的取值为准。这也提醒我们,动画参数往往需要肉眼微调,建议以transition中的代码为准进行调试。

whileHover:悬停放大效果

光有入场动画还不够,我们再为文本加上悬停反馈:当鼠标悬停在文字上时,文字像被“弹起”一样略微变大。

function App() { return ( <div className="App"> <motion.h1 ... // highlight-next-line whileHover={{ scale: 1.2 }} > Animation made easy with Framer Motion </motion.h1> </div> ); }

whileHover是 Framer Motion 的手势(gesture)props之一,它声明元素在悬停状态下应呈现的目标样式。这里scale: 1.2表示悬停时放大到原始尺寸的 1.2 倍,松开鼠标后自动回到原状,无需手动编写任何事件监听或 CSS 类切换。

图片动画:构建带旋转、拖拽与点按反馈的轮播

组织图片资源

首先在src目录下创建images文件夹存放图片,然后在src下新建image.js,集中导入所有图片:

import fox from "./images/pexels-fox-1071249.jpg"; import jill from "./images/jill.jpg"; import cha1 from "./images/cha1.jpg"; import cha2 from "./images/cha2.jpg"; export default [fox, jill, cha1, cha2];

把所有图片统一收拢到image.js再导出数组,可以避免在App.js中反复 import 同一批图片,减少冗余代码。

编写样式

App.css中加入轮播所需的基础样式:

.item { min-height: 10rem; min-width: 30rem; padding: 5px; } .item img { width: 60%; height: 50%; border-radius: 1rem; pointer-events: none; padding: 15%; } .inner-carousel { display: flex; background: rgb(76, 76, 76); height: 23rem; } .carousel { cursor: grab; overflow: hidden; background: rgb(215, 216, 215); height: 23rem; }

其中.carousel负责裁剪溢出内容并呈现“手抓”光标,.inner-carousel使用 flex 排列图片,.item img中的pointer-events: none避免图片自身拦截拖拽事件。

三层容器结构与旋转动画

修改App.js为如下代码,实现一个带旋转效果的轮播:

import { motion } from "framer-motion"; import "./App.css"; import images from "./image"; function App() { return ( <div className="App"> <motion.div className="carousel"> <motion.div animate={{ scale: [1, 1, 1, 1, 1], rotate: [0, 30, 60, 240, 360], }} > {images.map((image) => { return ( <motion.div whileHover={{ scale: 1.2 }} whileTap={{ scale: 2 }} className="item" transition={{ duration: 3 }} key={image} > <img src="https://refine.ams3.cdn.digitaloceanspaces.comundefined" alt="Carousel image placeholder" /> </motion.div> ); })} </motion.div> </motion.div> </div> ); } export default App;

整个动画的形态是:页面每次刷新时轮播执行一次旋转,鼠标靠近图片时触发悬停效果,触摸图片时触发点按效果。实现上需要三个容器,从外到内依次是外层轮播容器内层旋转容器图片容器,三者全部使用 Framer Motion 的motion组件层层嵌套:

  • 内层容器承担旋转与滑动效果,通过animateprop 将scale设为[1, 1, 1, 1, 1]rotate设为[0, 30, 60, 240, 360],让图片组完成一整圈 360° 的独特旋转;
  • 最内层图片容器负责逐张渲染从image.js导入的图片。

占位说明:原文示例中<img src>使用了占位 URL,实际接入时请改为src={image}引用images数组中映射出的真实图片地址,即可正确显示轮播内容。

拖拽与回弹:drag 系列 props

为了让轮播具备“可拖拽”的类轮播手感,给内层旋转容器加上dragdragConstraints

function App() { return ( <div className="App"> <motion.div className="carousel"> <motion.div ... // highlight-start drag="x" dragConstraints={{right: 0, left:-1100}} dragTransition={{ bounceStiffness: 600, bounceDamping: 8 }} // highlight-end > ... </motion.div> </motion.div> </div> ); }
  • drag="x":只允许沿 x 轴(水平方向)拖拽,因为轮播需要左右滑动;
  • dragConstraints={{ right: 0, left: -1100 }}:设置拖拽边界,让轮播向左最多移动 1100px,随后返回到原位,相当于一个行程挡板;
  • dragTransition={{ bounceStiffness: 600, bounceDamping: 8 }}:为拖拽末端增加弹跳感。bounceStiffness控制回弹的“刚性”(600 表示回弹较有力度),bounceDamping控制回弹阻尼(8 表示衰减较快、摆动几次即稳定),两者配合即可在每次拖到尽头时呈现自然的橡皮筋回弹效果。

悬停与点按:whileHover 与 whileTap

轮播中最内层的图片容器用于承载图片并定义交互动画:

function App() { return ( <div className="App"> <motion.div className="carousel"> <motion.div> {images.map((image) => { return ( <motion.div ... //highlight-start whileHover={{ scale: 1.2 }} whileTap={{ scale: 2 }} //highlight-end > <img src="https://refine.ams3.cdn.digitaloceanspaces.comundefined" alt="Carousel image placeholder" /> </motion.div> ); })} </motion.div> </motion.div> </div> ); }
  • whileHover={{ scale: 1.2 }}:鼠标悬停时图片最多放大到 1.2 倍;
  • whileTap={{ scale: 2 }}:按下/触摸时放大到 2 倍,释放后回弹,形成活泼的“点按弹跳”反馈;
  • 同时为该容器设置transition={{ duration: 3 }}(示例取值,可按需缩短至 0.3~0.5 秒获得更跟手的交互),让悬停与点按动画更平滑自然。

与纯 CSS 相比,以上旋转、拖拽、回弹、悬停、点按效果全部由几行 props 声明完成,无需维护复杂的@keyframes与手势状态机,这正是 Framer Motion 的核心价值。

仓库实证:Refine 文档站中的 Framer Motion 应用

Framer Motion 并非只在教程里出现,Refine 仓库的文档站点本身就重度使用它。这为理解该库的“真实项目用法”提供了第一手源码证据。

依赖版本

在 documentation/package.json 中可以看到文档站依赖声明"framer-motion": "^6.5.1",即当前仓库使用的是 Framer Motion 6.x 版本线。

博客阅读进度条:useScroll + useTransform

documentation/src/refine-theme/blog-header.tsx 中的BlogHeader组件演示了典型的滚动联动动画:

import { motion, useScroll, useTransform } from "framer-motion"; // ... const { scrollYProgress } = useScroll(); const progressPercentage = useTransform( scrollYProgress, [0.03, 0.95], ["0%", "100%"], );

useScroll()返回页面滚动进度 motion value,useTransform将其映射为百分比字符串,再通过style={{ width: progressPercentage }}绑定到motion.div上,形成随滚动推进的顶部进度条。这与教程中“把动画值绑定到元素样式”的思路一脉相承。

首页能力轮播:useMotionValue + useTransform + animate

documentation/src/refine-theme/landing-magic.tsx 中的LandingMagic组件在首页用useMotionValue(0)维护序列值,再用useTransform把序列映射为纵向位移百分比["0%", "-...%"],最后通过命令式animate(sequence, nextValue, { type: "spring", bounce: 0.35, stiffness: 50 })驱动一组数据源/UI 库图标循环滚动,效果与教程中的旋转轮播异曲同工——都用 motion value 驱动 transform 变换。

数字滚动计数器:animate + onUpdate

documentation/src/hooks/use-animated-counter.tsx 封装了一个useAnimatedCounterhook:

const controls = animate(initialValue, maxValue, { duration, ease: "easeOut", onUpdate(value) { setCounter(value); }, }); return () => controls.stop();

它用animate()在指定时长内从初始值插值到目标值,每帧通过onUpdate回调刷新 React state,并在卸载时调用controls.stop()清理动画。这是 Framer Motion 在非视觉、数值型动画场景下的典型用法。

抽屉与滚动偏移:useScroll

documentation/src/refine-theme/common-drawer.tsx 中的抽屉组件通过useScroll()监听scrollY,用scrollY.onChange动态计算顶部偏移量,保证抽屉在滚动条移动时仍然对齐公告栏——印证了 motion value 的可订阅特性在真实布局逻辑中的作用。

此外,documentation/src/refine-theme 目录下的doc-survey-widget.tsxdoc-thumbs-up-down-feedback-widget.tsxlanding-sweet-spot.tsx等组件还导入了AnimatePresencemotionuseInView,用于反馈小组件的进出场动画与视口检测,说明AnimatePresence(列表项/条件渲染元素的离场动画)和useInView(元素进入视口时触发动画)同样是日常项目的高频能力。

Framer Motion 常用 props 速查

结合教程与仓库源码,将本文涉及的 props 归纳如下:

Prop作用本文示例取值
motion.*将任意 HTML 元素包装为可动画组件motion.h1motion.div
initial定义动画的初始状态{ opacity: 0, scale: 0.5 }
animate定义动画目标状态或关键帧序列{ x: [50, 150, 50], opacity: 1, scale: 1 }{ rotate: [0, 30, 60, 240, 360] }
transition控制时长、延迟与缓动{ duration: 5, delay: 0.3, ease: [0.5, 0.71, 1, 1.5] }
whileHover悬停手势的目标状态{ scale: 1.2 }
whileTap点按/触摸手势的目标状态{ scale: 2 }
drag启用拖拽并限定轴向"x"
dragConstraints限制拖拽边界{ right: 0, left: -1100 }
dragTransition拖拽回弹物理参数{ bounceStiffness: 600, bounceDamping: 8 }
useScroll订阅页面/元素滚动进度const { scrollYProgress } = useScroll()
useTransform将一个 motion value 映射为另一个useTransform(scrollYProgress, [0.03, 0.95], ["0%", "100%"])
useMotionValue创建可订阅的数值源useMotionValue(0)
animate命令式地驱动动画animate(0, 100, { duration, onUpdate })

总结

本文从零开始演示了 Framer Motion 在 React 应用中的两种典型动画:用motion组件配合initial/animate/transition/whileHover完成的文本入场与悬停放大,以及用嵌套motion容器配合animate旋转关键帧、drag/dragConstraints/dragTransition拖拽回弹、whileHover/whileTap缩放反馈构建的图片轮播。相比手写 CSS 动画,这些效果仅需寥寥数行声明式 props 即可完成。

同时,Refine 文档站源码(blog-header.tsx、landing-magic.tsx、use-animated-counter.tsx、common-drawer.tsx)证明了这些 API 在生产环境中的真实价值:进度条、轮播、计数器、抽屉偏移乃至进离场动画,都可借助 Framer Motion 以声明式、低样板的方式落地。Framer Motion 还提供 Variants、AnimatePresenceuseInViewuseSpringuseVelocity等更强大的能力,值得在此基础上继续探索。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 20:01:04

短视频引流的卖家:流量来了,链接还没上

短视频引流的卖家&#xff1a;流量来了&#xff0c;链接还没上 一个短视频卖家的窘境&#xff1a; 「视频爆了&#xff01;一夜之间几十万播放&#xff0c;评论区全是『在哪买』。我火急火燎去上链接——结果越急越出错&#xff1a;属性填错、图片传歪、还赶上验证连环弹。等我…

作者头像 李华
网站建设 2026/9/10 20:00:04

Hermes Agent CLI实战:系统测试自动化流水线搭建指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 19:56:31

8款免费工具实测:如何有效降低AI生成内容的AI率

1. 为什么我们需要降低AI生成内容的"AI率"&#xff1f;最近两年&#xff0c;AI写作工具如雨后春笋般涌现&#xff0c;从ChatGPT到Claude&#xff0c;从文心一言到通义千问&#xff0c;这些工具确实极大提升了内容创作效率。但随之而来的是一个新问题——如何让AI生成…

作者头像 李华
网站建设 2026/9/10 19:55:06

cann/ge HCCL TP图Python样例

样例使用指导 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前…

作者头像 李华