使用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)。