React动效,真的有这么丝滑么

1,508 阅读3分钟

本文系翻译,有删改,原文见这里

“能快速看到编码效果”是很人选择从事前端岗位的初衷。

每个前端萌新都有个实现酷炫动效的理想。

使用React技术栈如何才能快速实现酷炫动效?今天向大家推荐一个动效库 —— Framer motion

怎么用?

Framer motion的核心API是motion的组件。每个HTMLSVG标签都有对应的motion组件。

他们渲染的结果与对应的原生组件完全一致,并在其之上增加了一些动画和手势相关的props

比如:

<motion.div />
<motion.span />
<motion.h1 />
<motion.svg />

mount动画

假设我们要实现一个组件mount时的下降显现效果,需要使用motion组件的initialanimate属性。

  • initial定义组件的初始状态。

  • animate定义组件mount时的动画效果。如果其值与initial不同,则会产生过渡的动画效果。

import { motion } from "framer-motion"

<motion.div
  initial={{ opacity: 0, y: -50 }}
  animate={{ opacity: 1, y: 0 }}  
>
  Hello World!
</motion.div>

Framer Motion检测到initialanimate中有value不同的key,对这些key执行过渡效果。

例子中y轴方向距离会从-50变为0,透明度从0变为1。

unmount动画

在做删除所选项或翻页时,组件unmount时的动画效果很重要。

为了实现unmount动画效果,需要将组件包裹在<AnimatePresence/>内。

这是因为我们需要延迟组件unmount的时机,这样才有时间展示消失动画。

就像initial对应animate的渐变,我们需要指定exit属性,当组件unmount时,会执行从animateexit的动画效果。

import { motion } from "framer-motion"

<AnimatePresence>
    <motion.div
      exit={{ opacity: 0, y: -50 }}
      initial={{ opacity: 0, y: -50 }}
      animate={{ opacity: 1, y: 0 }}    
    >
      Hello World!
    </motion.div>
</AnimatePresence>

效果:

编排动画

Framer Motion的一个强大功能是:通过variants属性来编排不同组件的动效。

下面的代码通过variants属性实现上文mount时同样的效果。

const variants = {
  visible: { opacity: 0, y: -50 },
  hidden: { opacity: 1, y: 0 },
}

<motion.div initial="hidden" animate="visible" variants={variants} />

这里传给initialanimate的是字符串,其中hidden字符串指代variants.hidden对象。

这是varients刻意要求的。当motion组件有children时,拥有animate属性的子孙motion组件也能响应同样的效果。

假设我们要实现如下效果:

每个卡片组件有偏移、渐入的mount时动画,同时每个卡片组件在前一个卡片进入后的0.1秒后进入。

为了实现这个效果,我们先为卡片、容器组件实现对应的variants

const variants = {
  // 容器对应的variants效果
  container: {  
  },
  // 卡片对应的variants效果
  card: { 
  }
};

其中卡片有x轴的偏移和opacity的改变:

const variants = {
  container: {
  },
  card: {
    initial: {
      opacity: 0,
      x: -50
    },

    animate: {
      opacity: 1,
      x: 0
    }
  }
};

容器通过animate.transition.staggerChildren指明每个motion子组件的过渡间隔时间:


const variants = {
  container: {
    animate: {
      transition: {
        staggerChildren: 0.1
      }
    }
  },
  card: {
    initial: {
      opacity: 0,
      x: -50
    },

    animate: {
      opacity: 1,
      x: 0
    }
  }
};

对应组件:


const StaggeredList = () => {
  return (
    <motion.div
      initial="initial"
      animate="animate"
      variants={variants.container}     
    >
      {new Array(5).fill("").map(() => {
        return <Card />;
      })}
    </motion.div>
  );
};

const Card = () => (
  <motion.div
    variants={variants.card}   
  >
    Hello World!
  </motion.div>
);

其他可选库

上文我们介绍了Framer motion的基本使用。除此以外,React Spring也是React技术栈优秀的动效库。

Framer Motion简单易懂,同时支持更多动画类型,如:

  • 弹力

  • 补间动画

  • 惯性运动

他的缺点是缺少文档,并且某些属性对SVG无效。

React Spring是一个基于弹性力学的动画库。通过他,可以更灵活的实现Framer Motion的所有效果。但是学习曲线很陡峭。

建议做复杂自定义动画时可以考虑(尤其是SVG3D)。