79
67

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ReactのアニメーションにReact Poseが便利

Last updated at Posted at 2018-07-17

Poseというライブラリがシンプルに書けて便利だったので、いくつか実装例を紹介します。また最後の方にstyled-componentsと組み合わせた時の体験もよかったので、その方法についても書きます。

ちなみにReact v16.3以上 がpeer dependencyとしてrequiredされているので残念ながら様々な事情により古いバージョンにバインドされている方は使えなさそうです。

ちなみに記事の内容はほぼドキュメントなぞってるだけなのでよければ本家も。
Pose Documentation

基本的なトランジション

それではまずは簡単なトランジションの書き方から。まずは設定を書きます。実際にトランジションさせるときは、このvisiblehiddenという名前を切り替えて行います。

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;
}

できました。

pose basic.gif

他にも出てくる時にx座標をずらして右からシュッと出したりみたいなのも簡単にできます。

const props = {
  visible: { 
    opacity: 1,
    x: 0
  },
  hidden: { 
    opacity: 0,
    x: '100%'
  }
}

pose basic2.gif

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
  }
};

これで動かしてみます。

pose basic3.gif

ちゃんと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>
    );
  }
}

pose list.gif

簡単ですね。ありがたや。

Drag

ドラッグもオプションを追加するだけで実現できます。先ほどのBoxにdraggable というプロパティを足してあげましょう。あとビュンビュン動かれるとドラッグしづらいのでsetIntervalしてる部分をコメントアウトします。

const props = {
  draggable: true
};

これだけで好き放題ドラッグさせることが可能になりました。
pose basic4.gif

方向をx方向だけ(もしくはy方向だけ)に制限することや、動かせる幅を制限したりもできます。

const props = {
  draggable: 'x',
  dragBounds: { left: -100, right: 100 }
};

x 軸に左右100pxだけドラッグ可能にした図↓

pose basic5.gif

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>
    );
  }
}

pose dropdown.gif

styled-componentsでのアニメーションの書き味が別段悪いわけではないですが、Poseと組み合わせることによってより強力になるなと感じました。

感想

いくつか例を書いてみましたが、この記事で触れたのは本当にベーシックな部分だけなのでご興味あれば本家のサイトを見てご自分で色々遊んで見てください!
Pose

オサレなLPとかでやるようなゴリゴリのアニメーションが書けるかはわかりませんが、普通のウェブアプリでやるようなアニメーション・トランジションは全然シンプルに書けるなと思いました。

79
67
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
79
67

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?