使用ReactMotion在React中制作动画
React Motion 是一个流行的 React 动画库。 它利用物理来创建感觉自然的动画。 要创建逼真的动画,我们所要做的就是为 stiffness 和 damping 提供值,其余的由 React Motion 负责。
在这篇文章中,我们将介绍使用该库为简单 Card 组件的缩放设置动画的基础知识。 我们将使用 styled-components 作为 Card 的样式。 然后,我们将通过一个简单的示例,将组件动画化为状态的函数。
安装
只需使用 npm 或 Yarn 将 react-motion 包添加到您的项目中。 在这里,我们还将使用 styled-component,因此我们也将添加它:
$ yarn add react-motion styled-components # or $ npm install react-motion styled-components
设置
为了创建一个简单的演示,我们的 App 组件将只渲染两个 Card 组件:
应用程序.js
import React, { Component } from 'react'; import { injectGlobal } from 'styled-components'; import Card from './Card'; injectGlobal` body { margin: 0; background: #fbfbfb; } `; class App extends Component { render() { return ( <React.Fragment> <Card /> <Card title="😎 Fancy!" content="Nothing to say" /> </React.Fragment> ); } } export default App;
注意我们如何使用样式组件的 injectGlobal 来注入一些全局样式。
以下是构成我们的 Card 组件的所有组件:
Card.js
import React from 'react'; import styled from 'styled-components'; const CardWrapper = styled.div` background: #fff; max-width: 500px; margin: 2rem auto; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 5px; `; const FooterWrapper = styled.div` border-top: 2px solid #f7f7f7; padding: 1rem 0; text-align: center; `; const HeaderWrapper = styled.div` background-image: url('/palm-trees.jpg'); min-height: 150px; color: white; text-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); background-size: 100%; background-position: 50%; display: flex; justify-content: flex-end; align-items: flex-end; padding: 1rem; `; const MainWrapper = styled.div` padding: 1rem; `; const Button = styled.button` background-image: linear-gradient(to bottom, #fff, #f3f3f3); border-radius: 8px; letter-spacing: 1px; padding: 10px 20px; margin: 0 0.45rem; border: 1px solid #ddd; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); &:active { background: #eee; } `; const Header = ({ title }) => { return ( <HeaderWrapper> <h1>{title}</h1> </HeaderWrapper> ); }; const Main = ({ content }) => { return ( <MainWrapper> <p>{content}</p> </MainWrapper> ); }; const Footer = () => { return ( <FooterWrapper> <Button>View</Button> <Button>Save for later</Button> </FooterWrapper> ); }; class Card extends React.Component { render() { const { title, content } = this.props; return ( <CardWrapper> <Header title={title} /> <Main content={content} /> <Footer /> </CardWrapper> ); } } Card.defaultProps = { title: 'My card title', content: 'Bacon ipsum dolor amet pork chop pork shoulder.' }; export default Card;
我们的卡片如下所示:
输入反应运动
现在,假设我们可以在卡片首次安装时为其设置动画。 让我们使用 React Motion 的 Motion 组件来完成这个:
应用程序.js
// ... import { Motion, spring } from 'react-motion'; import Card from './Card'; // ... const AnimatedCard = props => { return ( <Motion defaultStyle={{ scale: 0.5 }} style={{ scale: spring(1, { stiffness: 60, damping: 10 }) }} > {interpolatedStyle => <Card scale={interpolatedStyle.scale} {...props} />} </Motion> ); }; class App extends Component { render() { return ( <React.Fragment> <AnimatedCard /> <AnimatedCard title="😎 Fancy!" content="Nothing to say" /> </React.Fragment> ); } } export default App;
如您所见,Motion 组件使用了 render prop 模式。 它期望一个函数作为其 children 属性,并且该函数接收包含当前插值的 interpolatedStyle
。 在这里,我们将插值的 scale 值传递给 scale 属性。
我们可以在样式组件中使用这个 scale 属性,如下所示:
Card.js
// ... const CardWrapper = styled.div` background: #fff; max-width: 500px; margin: 2rem auto; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 5px; transform: ${props => `scale(${props.scale})`}; `; // ... class Card extends React.Component { render() { const { title, content, scale } = this.props; return ( <CardWrapper scale={scale}> <Header title={title} /> <Main content={content} /> <Footer /> </CardWrapper> ); } } // ...
Motion 组件采用可选的 defaultStyle 属性和必需的 style 属性。 style 属性是使用 spring 辅助函数派生的。 Spring 接受一个值,以及一个可选的配置对象,其值为 stiffness、damping 和 precision。 当没有配置对象传递给 spring 时,刚度默认为 170,阻尼默认为 26。
在前面的示例中,我们传递了一个 scale 属性,然后被样式组件使用,但我们也可以为内联样式设置动画。 例如,这里我们渲染一个弹出到视图中的 h1 元素:
const FancyTitle = () => { return ( <Motion defaultStyle={{ left: -100 }} style={{ left: spring(10) }}> {val => <h1 style={{ position: 'absolute', ...val }}>Hello!{' '} <span role="img" aria-label="Hand wave"> 👋 </span> </h1>} </Motion> ); };
作为状态函数的动画
使用 React Motion 可以很容易地定义作为状态函数的动画。
在下面的示例中,我们首先在 App 组件挂载时将 h1 元素动画到视图中,然后提供调用方法来更改状态的按钮,从而控制元素上的动画:
import React, { Component } from 'react'; import { Motion, spring } from 'react-motion'; class App extends Component { state = { left: 0 }; handleClick = val => { if (val && !isNaN(val)) { this.setState({ left: +val }); } }; reset = () => this.setState({ left: 0 }); render() { return ( <React.Fragment> <Motion defaultStyle={{ left: -100 }} style={{ left: spring(this.state.left) }} > {val => ( <h1 style={{ position: 'absolute', ...val }}> Hello!{' '} <span role="img" aria-label="Hand wave"> 👋 </span> </h1> )} </Motion> <input type="number" placeholder="enter a value" ref={input => (this.input = input)} /> <button onClick={() => this.handleClick(this.input.value)}>Set</button> <button onClick={this.reset}>Reset</button> </React.Fragment> ); } } export default App;
💃 现在开始行动吧! 在以后的文章中,我们将探讨 React Motion 提供的其余 API(StaggeredMotion、TransitionMotion 和 presets)。