Mithril 动画

动画

技术选型

动画可以使应用更生动。现在的浏览器对 CSS 动画的支持已经很好了,而且还有各种可以提供基于 JavaScript 的动画。如果你喜欢最前沿的技术的话,还有 Web APIpolyfill

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 提供了一个 onbeforeremove 钩子,允许推迟元素的移除。

我们来为 exit 类添加动画,使它的 opacity 属性从 1 到 0 渐变。

.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}}, "Toggle"),
            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">),我们在该元素上添加了一个类 exit

我们返回一个 Promise,并在半秒钟后完成。当我们从 onbeforeremove 返回 promise 时,Mithril 会保持等待,直到 promise 被完成,元素才会被移除。在这个例子中,它等待了半秒,给了 exit 足够的时间来完成动画。

我们把 Toggler 组件渲染到页面上,就能测试这个渐变动画效果了:

m.mount(document.body, Toggler)

性能

使用 CSS 动画时,建议你只在 opacitytransform 属性上使用动画,因为这两个属性的动画会启用硬件加速,会比 topleftwidthheight 属性的动画有更高的性能。

同时也建议你避免使用 box-shadow 属性和类似 :nth-child 这样的选择器,因为这些属性的动画也是很占用资源的。如果你想在 box-shadow 属性上使用动画,建议box-shadow 属性放在伪元素中,并用伪元素的 opacity 的变化来代替 box-shadow 的变化。其他比较耗费资源的包括:大型图片或者动态可缩放的图片、使用不同 position 的值进行重叠的元素。