Poseというライブラリがシンプルに書けて便利だったので、いくつか実装例を紹介します。また最後の方にstyled-componentsと組み合わせた時の体験もよかったので、その方法についても書きます。
ちなみにReact v16.3以上 がpeer dependencyとしてrequiredされているので残念ながら様々な事情により古いバージョンにバインドされている方は使えなさそうです。
ちなみに記事の内容はほぼドキュメントなぞってるだけなのでよければ本家も。
Pose Documentation
基本的なトランジション
それではまずは簡単なトランジションの書き方から。まずは設定を書きます。実際にトランジションさせるときは、このvisible
とhidden
という名前を切り替えて行います。
import React from 'react';
import posed from 'react-pose';
const props = {
visible: { opacity: 1 },
hidden: { opacity: 0 }
}
そしてposed
という関数でそのトランジションの設定を持ったコンポーネントを作成します。
const Box = posed.div(props)
それではこのコンポーネントのトランジションを1秒ごとに切り替えてみましょう。
export default class Example extends React.Component {
state = { isVisible: false };
componentDidMount() {
setInterval(() => {
this.setState({ isVisible: !this.state.isVisible });
}, 1000);
}
return (
<Box className="box" pose={this.state.isVisible ? 'visible' : 'hidden'} />
);
}
ちなみにCSSはこんな感じでただのトマト色の四角い箱を描きます。
.box {
display: block;
height: 200px;
width: 200px;
background-color: tomato;
}
できました。
他にも出てくる時にx座標をずらして右からシュッと出したりみたいなのも簡単にできます。
const props = {
visible: {
opacity: 1,
x: 0
},
hidden: {
opacity: 0,
x: '100%'
}
}
transition
プロパティの設定
CSSで書くようなtransition
プロパティも指定することもできます。トランジションの長さだったりeaseを指定したい場合はこれを用います。
const props = {
visible: {
opacity: 1,
transition: {
ease: 'easeOut',
duration: 300
}
},
hidden: {
opacity: 0
}
}
書き味はcssでアニメーション書く時とあまり変わらないので馴染みやすいと思います。
動的にアニメーションのプロパティを変更する
上述のprops
というオブジェクトに書いていた設定の値には関数を入れることができ、その関数はアニメーション実行時に評価されるため動的に値を変えることができます。またpropsを用いてコンポーネントからアニメーションの設定に関わる値を入れることができるので、そのやり方も合わせて見てみましょう。
まず普通のReactコンポーネント同様にpropsを書きます。offset
というプロパティを追加しました。
render() {
return (
<Box
className="box"
pose={this.state.isVisible ? 'visible' : 'hidden'}
offset={1000}
/>
);
}
次に設定の方で、propsから値を受け取りたいところに関数を書きます。関数を書くとアニメーションが実行される際にprops
が引数に入ってくるので、それを用いて値をセットしています。
const props = {
visible: {
opacity: 1,
x: 0
},
hidden: {
opacity: 0,
x: ({ offset }) => offset
}
};
これで動かしてみます。
ちゃんとpropsに指定した1000pxが渡ってビュンビュンしてるのが見て取れますね。
Listのアニメーション
リストで子要素ごとに間隔空けてアニメーションしたいこと、あると思います。
delayChildren(親要素がアニメーションしてから子要素がアニメーションを開始するまでの時間)やstaggerChildren(子要素間のアニメーションのdelay)などのプロパティがあるので、それらを指定することによって簡単に実現できます。
まずは設定を書きます。
const sidebarProps = {
open: {
x: '0%',
delayChildren: 300,
staggerChildren: 60
},
closed: {
delay: 500,
staggerChildren: 20,
x: '-100%'
}
};
const itemProps = {
open: { opacity: 1, y: 0 },
closed: { opacity: 0, y: 20 }
};
それを使ったコンポーネントを使ってあとは先ほどと同じ要領でpose
を切り替えます。
const SidePanel = posed.ul(sidebarProps);
const Item = posed.li(itemProps);
export class PoseList extends React.Component {
state = { isOpen: false };
componentDidMount() {
setTimeout(this.toggle, 1000);
}
toggle = () => this.setState({ isOpen: !this.state.isOpen });
render() {
return (
<div>
<button onClick={this.toggle}>押す</button>
<SidePanel
onClick={this.toggle}
className="sidebar"
pose={this.state.isOpen ? 'open' : 'closed'}
>
<Item className="item" />
<Item className="item" />
<Item className="item" />
<Item className="item" />
<Item className="item" />
<Item className="item" />
</SidePanel>]
</div>
);
}
}
簡単ですね。ありがたや。
Drag
ドラッグもオプションを追加するだけで実現できます。先ほどのBoxにdraggable
というプロパティを足してあげましょう。あとビュンビュン動かれるとドラッグしづらいのでsetInterval
してる部分をコメントアウトします。
const props = {
draggable: true
};
方向をx方向だけ(もしくはy方向だけ)に制限することや、動かせる幅を制限したりもできます。
const props = {
draggable: 'x',
dragBounds: { left: -100, right: 100 }
};
x 軸に左右100pxだけドラッグ可能にした図↓
styled-componentsと組み合わせる
最後に、Pose本体の使い方からは話がそれるんですが、styled-componentsとPoseで作ったコンポーネントを組み合わせてみようと思います。組み合わせると言っても特に難しいことは何もなく、やることは単なるコンポーネントのコンポジションです。
試しに上からニュッと出てくるドロップダウン作ってみました。
import React from 'react';
import styled from 'styled-components';
import posed from 'react-pose';
const props = {
visible: {
opacity: 1,
originY: 0,
scaleY: 1
},
hidden: {
opacity: 0,
scaleY: 0
}
};
const Dropdown = posed.ul(props);
const StyledDropdown = styled(Dropdown)`
display: 'block';
position: absolute;
top: 20px;
width: 250px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.24);
padding-left: 0;
`;
const Item = styled.li`
list-style: none;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
font-size: 14px;
color: #666;
padding: 5px 10px;
cursor: pointer;
&:hover {
background-color: rgba(0, 0, 0, 0.06);
}
`;
export default class Example extends React.Component {
state = { isVisible: false };
toggle = () => {
this.setState({ isVisible: !this.state.isVisible });
};
render() {
return (
<div>
<button onClick={this.toggle}>押す</button>
<StyledDropdown pose={this.state.isVisible ? 'visible' : 'hidden'}>
<Item>エビデイ</Item>
<Item>エビナイ</Item>
<Item>トラックメイカー</Item>
</StyledDropdown>
</div>
);
}
}
styled-componentsでのアニメーションの書き味が別段悪いわけではないですが、Poseと組み合わせることによってより強力になるなと感じました。
感想
いくつか例を書いてみましたが、この記事で触れたのは本当にベーシックな部分だけなのでご興味あれば本家のサイトを見てご自分で色々遊んで見てください!
Pose
オサレなLPとかでやるようなゴリゴリのアニメーションが書けるかはわかりませんが、普通のウェブアプリでやるようなアニメーション・トランジションは全然シンプルに書けるなと思いました。