使用ReactMotion在React中制作动画

来自菜鸟教程
跳转至:导航、​搜索

React Motion 是一个流行的 React 动画库。 它利用物理来创建感觉自然的动画。 要创建逼真的动画,我们所要做的就是为 stiffnessdamping 提供值,其余的由 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 接受一个值,以及一个可选的配置对象,其值为 stiffnessdampingprecision。 当没有配置对象传递给 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(StaggeredMotionTransitionMotionpresets)。