React Motion は、Reactのアニメーションで人気のあるライブラリです。 物理学を利用して、自然に感じるアニメーションを作成します。 リアルなアニメーションを作成するために必要なのは、剛性ダンピングの値を提供することだけで、残りはReactMotionが処理します。

この投稿では、ライブラリを使用して単純なCardコンポーネントのスケーリングをアニメーション化する基本について説明します。 カードのスタイリングには、スタイルコンポーネントを使用します。 次に、状態の関数としてコンポーネントをアニメーション化する簡単な例を見ていきます。

インストール

npmまたはYarnを使用して、react-motionパッケージをプロジェクトに追加するだけです。 ここでは、 styled-component も使用するので、それも追加します。

$ yarn add react-motion styled-components

# or
$ npm install react-motion styled-components

設定

簡単なデモを作成するために、Appコンポーネントは2つのCardコンポーネントをレンダリングするだけです。

App.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;

style-componentのinjectGlobalを使用していくつかのグローバルスタイルを挿入する方法に注目してください。


カードコンポーネントを構成するすべてのコンポーネントは次のとおりです。

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;

カードは次のようになります。

Example card components

ReactMotionを入力してください

ここで、カードが最初にマウントされたときにカードをアニメーション化できるとしましょう。 これを実現するために、ReactMotionのMotionコンポーネントを使用してみましょう。

App.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コンポーネントはrenderpropパターンを利用しています。 関数は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 propは、springヘルパー関数を使用して派生します。 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 プリセット)について説明します。