Mithril 1.1.0

アニメーション


技術の選択

アニメーションはアプリケーションのいきいきとさせるためによく利用されます。今日のブラウザはCSSアニメーションをサポートしていますし、JavaScriptベースの高速なアニメーションをサポートするさまざまなライブラリもあります。また、最先端のものが好きであれば、これから登場するWeb APIとそのpolyfillもあります。

Mithrilそのものはアニメーションのための APIを提供していませんが、これらのさまざまなライブラリを活用することで、よりリッチで複雑なアニメーションが実現できます。Mithril自身は、以前はアニメーションが扱いにくかったいくつかのケースに対して、とても簡単に扱えるようなフックを提供しています。


エレメント作成時のアニメーション

要素作成時にCSSを使って要素をアニメーションさせるのは、これ以上簡単にはできないでしょう。CSSクラスにアニメーションを追加するだけです。

.fancy {animation:fade-in 0.5s;}
@keyframes fade-in {
    from {opacity:0;}
    to {opacity:1;}
}
var FancyComponent = {
    view: function() {
        return m(".fancy", "Hello world")
    }
}

m.mount(document.body, FancyComponent)

要素の削除時のアニメーション

以前のバージョンでは、要素の削除時にアニメーションさせるのは、本当の削除が行われるまで実際の削除を遅延させるというややこしい実装が必要でした。幸い、Mithril 1.0以降ではonbeforeremoveフックを提供しており、要素の削除を遅延させるのが簡単に行えるようになりました。

それではopacityを1から0までフェードさせるexitアニメーションを作成してみましょう。

.exit {animation:fade-out 0.5s;}
@keyframes fade-out {
    from {opacity:1;}
    to {opacity:0;}
}

それでは前のセクションで作成したFancyComponentコンポーネントを表示したり隠したりするコンポーネントを作成してみましょう。

var on = true

var Toggler = {
    view: function() {
        return [
            m("button", {onclick: function() {on = !on}}, "トグル"),
            on ?m(FancyComponent) : null,
        ]
    }
}

次に、FancyComponentを修正し、削除時にフェードアウトさせるようにします:

var FancyComponent = {
    onbeforeremove: function(vnode) {
        vnode.dom.classList.add("exit")
        return new Promise(function(resolve) {
            setTimeout(resolve, 500)
        })
    },
    view: function() {
        return m(".fancy", "Hello world")
    }
}

vnode.domはコンポーネントのルートとなるDOMエレメント(<div class="fancy">)を指しています。classList APIを使って、exitクラスを<div class="fancy">に付与しています。

その後、0.5秒後に解決されるPromiseを返しています。onbeforeremoveからPromiseを返すと、MithrilはそのPromiseが解決されるまで要素の削除を待ちます。このサンプルでは、終了時のアニメーションが完了するのとちょうど同じ時間である0.5秒待っています。

Togglerコンポーネントをマウントすると、作成時と終了時の両方のアニメーションが動作することが確認できます。

m.mount(document.body, Toggler)

注意点としては、onbeforeremoveフックは、DOMから要素が切り離されてparentNodeがいなくなる要素に対してのみトリガーされます。この動作は設計上意図されたものです。ラウトの変更などで、ページ上のすべての要素の終了アニメーションが実行されてしまってユーザ経験が悪化するのを防ぐためです。終了アニメーションが実行されない場合は、ツリーの上位の削除される要素にonbeforeremoveハンドラーを移動して、アニメーションが実行されるようにしてください。


パフォーマンス

アニメーションを作成する場合は、opacitytransformのCSSルールだけを使うことをおすすめします。現代のブラウザであればハードウェアアクセラレーションが行えるため、topleftwidthheightを操作するよりも良いパフォーマンスが得られます。

また、box-shadowルールや、:nth-childなどのセレクターを使うとパフォーマンスに大きな影響があります。もしbox-shadowをアニメーションさせたいのであれば、疑似要素にbox-shadowのルールを適用し、その疑似要素のopacityをアニメーションさせる方法を検討してください。それ以外では巨大な画像を使ったり、ダイナミックに画像をスケールさせたり、positionの値が異なる要素同士のオーバーラップ(fixedに対してabsoluteが重なる)も高コストになります。


License: MIT. © Leo Horie.