多元素动画

上面讲的都是针对单个元素进行动画,如果你将Animate嵌套使用,那么父Animate组件还能充当动画管理者,它可以控制所有子Animate组件的动画。

  1. <div>
  2. <button ev-click={self.set.bind(self, 'show', !self.get('show'))}>切换</button>
  3. <Animate>
  4. <Animate v-if={self.get('show')} key="show">show</Animate>
  5. <Animate v-else key="hide">hide</Animate>
  6. </Animate>
  7. </div>

多元素动画,你必须为每一个子Animate组件指定一个唯一的key

Animate多元素动画时,会默认给离开的元素设置position: absolute,所以你会看到上述例子中,动画元素是重叠的。如果你不想元素离开时绝对定位,你可以设置动画管理者Animate组件的a:movefalse

a:move的作用下面会讲到

动画模式

Animate管理的子元素,进入/离开是同时进行的,通过动画模式属性a:mode,你可以改变这一规则。它的取值为:

  1. both 默认模式,同时进入/离开
  2. out-in 旧元素离开后,新元素再进入
  3. in-out 新元素进入后,旧元素再离开

使用out-in的例子

<Animate a:mode="out-in" a:move={false}>
    <Animate a:tag="button" v-if={!self.get('disabled')}
        ev-click={self.set.bind(self, 'disabled', true)}
        key="on"
        class="static"
    >on</Animate>
    <Animate a:tag="button" v-else
        ev-click={self.set.bind(self, 'disabled', false)}
        key="off"
        class="static"
    >off</Animate>
</Animate>

列表动画

多元素动画最常见的使用场景是列表渲染。例如:

.list div {
    display: inline-block;
    padding: 5px 10px;
    border: 1px solid #eee;
    margin: 5px;
}
.list-enter, .list-leave {
    opacity: 0;
    transform: translateY(20px);
}
.list-enter-active, .list-leave-active, .list-move {
    transition: all 1s;
}
<div>
    <button ev-click={self.add.bind(self)}>添加</button>
    <button ev-click={self.remove.bind(self)}>删除</button>
    <Animate a:move={false} class="list">
        <Animate v-for={self.get('list')}
            key={value} 
            a:transition="list"
        >{value}</Animate>
    </Animate>
</div>
Intact.extend({
    template: template,
    defaults: function() {
        this.nextNum = 6;
        return {
            list: [1, 2, 3, 4, 5]
        };
    },
    randomIndex: function() {
        return Math.floor(Math.random() * this.get('list').length);
    },
    add: function() {
        var list = this.get('list').slice(0);
        list.splice(this.randomIndex(), 0, this.nextNum++);
        this.set('list', list);
    },
    remove: function() {
        this.get('list').splice(this.randomIndex(), 1);
        this.update();
    }
});

列表位移动画

上述例子存在一个问题:元素插入和删除时,兄弟元素的位置是瞬间移动的,这样显得很突兀。通过设置列表位移动画,可以使兄弟元素的移动也加入动画。而该功能默认是开启的,就是上面提到的a:move属性。它对应的css类名为animate-move,你只需要为该类名添加transition样式即可。

.animate-move {
    transition: transform 1s;
}
<div>
    <button ev-click={self.shuffle.bind(self)}>打乱</button>
    <button ev-click={self.add.bind(self)}>添加</button>
    <button ev-click={self.remove.bind(self)}>删除</button>
    <Animate class="list">
        <Animate v-for={self.get('list')} key={value} a:transition="list">
            {value}
        </Animate>
    </Animate>
</div>
Intact.extend({
    template: template,
    defaults: function() {
        this.nextNum = 6;
        return {
            list: [1, 2, 3, 4, 5]
        };
    },
    shuffle: function() {
        this.set('list', _.shuffle(this.get('list')));
    },
    randomIndex: function() {
        return Math.floor(Math.random() * this.get('list').length);
    },
    add: function() {
        var list = this.get('list').slice(0);
        list.splice(this.randomIndex(), 0, this.nextNum++);
        this.set('list', list);
    },
    remove: function() {
        this.get('list').splice(this.randomIndex(), 1);
        this.update();
    }
});

动画模式与列表动画结合

动画模式a:mode不仅仅只支持单个元素,对于列表动画也支持。

<div>
    <button ev-click={self.addAndRemove.bind(self)}>添加一个同时删除一个</button>
    <Animate a:mode="in-out" class="list">
        <Animate v-for={self.get('list')} key={value} a:transition="list">
            {value}
        </Animate>
    </Animate>
</div>
Intact.extend({
    template: template,
    defaults: function() {
        this.nextNum = 6;
        return {
            list: [1, 2, 3, 4, 5]
        };
    },
    randomIndex: function(num) {
        return Math.floor(Math.random() * (this.get('list').length + num));
    },
    addAndRemove: function() {
        var list = this.get('list').slice(0);
        var addIndex = this.randomIndex(0);
        var removeIndex = this.randomIndex(1);
        while (removeIndex === addIndex) removeIndex = this.randomIndex(1);
        list.splice(addIndex, 0, this.nextNum++);
        list.splice(removeIndex, 1);
        this.set('list', list);
    }
});

Animate仅支持DOM元素的动画,暂不支持组件的动画