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コンポーネントをレンダリングするだけです。
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を使用していくつかのグローバルスタイルを挿入する方法に注目してください。
カードコンポーネントを構成するすべてのコンポーネントは次のとおりです。
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;
カードは次のようになります。
ReactMotionを入力してください
ここで、カードが最初にマウントされたときにカードをアニメーション化できるとしましょう。 これを実現するために、ReactMotionのMotionコンポーネントを使用してみましょう。
// ...
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プロップは、次のようなスタイルコンポーネントで使用できます。
// ...
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 、プリセット)について説明します。