Reactでanime.jsを使ってみる

こんにちは🤠
iOSエンジニアのmuukiiです🤠

最近は業務でReactを少し触ることがあるのですが、まだまだ理解していないことが多くて苦戦中です😵

学習中なので、そのメモとして記事を書こうと思います。
今回はReactとanime.jsの連携についての話をします。

anime.jsについては下記リンクを参照してください。

anime.jsの使い方は次のような雰囲気です。

anime({
targets: '#cssSelector .el',
translateX: 250
});

targetsにはCSS SelectorもしくはJavaScriptで取得したDOM Node を指定することができます。配列で与えることも可能です。

上記の例はCSS Selectorを指定する例でしたが、DOM Nodeを指定する例は次のようになります。

const el = document.querySelector('#domNode .el');

const domNode = anime({
targets: el,
translateX: 250
});

ReactのComponentやElementにアニメーションを指定する

CSS Selectorを使ってアニメーションを行う場合は問題なく実現はできますが、特定のComponentやElementにアニメーションを指定したい場合はrenderされた実際のDOM Nodeを取得する必要があります。

Componentが持つDOM Nodeを取得するにはRefsを使用します。

次のように記述することでMyComponentがマウントされたタイミングでdivがアニメーションを開始します。

import * as React from 'react'
import anime from 'animejs'
class MyComponent extends React.Component {
render() {
return (
<div
ref={ref => {
anime({
targets: ref,
translateX: 250,
})
}}
/>
)
}
}

マウントされる以外のタイミングでアニメーションを実行したい場合はrefMyComponent内で保持しておくことで実現できます。

Refsの扱いには注意が必要

Refsは場合によって返却されるものが異なります。先ほどの例で使用しているdiv はelementに相当するものなのでrefの実体はDOM Nodeになります。

しかし、ComponentやHOCではrefの挙動は異なります。

class Box extends React.Component {
render() {
return <div>box</div>
}
}
class Container extends React.Component {

render(){
return <Box ref={ ref => { console.log(ref) }}>foo</Box>
}
}

この例ではrefはBox-Component自体を指すのでDOM Nodeではありません。

この場合は、ReactDOMを使ってrefを元にDOM Nodeを取得します。

class Box extends React.Component {
render() {
return <div>box</div>
}
}
class Container extends React.Component {
render() {
return (
<Box
ref={ref => {
const dom = ReactDOM.findDOMNode(ref)
anime({
targets: dom,
translateX: 250,
})
}}
>
foo
</Box>
)
}
}

これでComponentの場合もアニメーションが可能になります。

このRefsの挙動の違いにはstyled-componentsで定義したComponentにアニメーションを適用しようとした時に気づいて調べていたのですが、styled-componentsの場合はinnerRef というプロパティが実装されており、こちらを使用することで直接DOM Nodeを受け取ることが可能でした。

HOCの場合はrefをもらうことができない様子。(まだ詳しく調べてないです。)

おわりに

anime.jsをReactで使用するためにreact-animeというライブラリが公開されていますが、Reactを使用しながらDOMとの連携を行う方法を知っておきたかったので今回のようなアプローチについて書きました。