Vue学习笔记进阶篇——列表过渡及其他

本文为转载,原文:Vue学习笔记进阶篇——列表过渡及其他
本文将介绍Vue中的列表过渡,动态过渡, 以及可复用过渡是实现。

列表过渡

目前为止,关于过渡我们已经讲到:

  1. 单个节点
  2. 同一时间渲染多个节点中的一个

那么怎么同时渲染整个列表,比如使用 v-for ?在这种场景中,使用 <transition-group>组件。在我们深入例子之前,先了解关于这个组件的几个特点:

  1. 不同于 <transition>, 它会以一个真实元素呈现:默认为一个<span>。你也可以通过 tag 特性更换为其他元素。
  2. 内部元素 总是需要 提供唯一的 key属性值.

列表的进入和离开过渡

现在让我们由一个简单的例子深入,进入和离开的过渡使用之前一样的 CSS 类名。

<div id="app1">
    <button @click="add">Add</button>
    <button @click="remove">Remove</button>
    <transition-group name="list" tag="p">
        <span v-for="item in items" :key="item" class="list-item">
            {{item}}
        </span>
    </transition-group>
</div>
.list-item{
            display: inline-block;
            margin-right: 10px;
        }
        .list-enter-active, .list-leave-active{
            transition: all 1s;
        }
        .list-enter, .list-leave-to{
            opacity: 0;
            transform: translateY(30px);
        }
var app1 = new Vue({
    el:'#app1',
    data:{
        items:[1,2,3,4,5,6,7,8,9],
        nextNum:10
    },
    methods:{
        randomIndex:function () {
            return Math.floor(Math.random() * this.items.length)
        },
        add:function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
        },
        remove:function () {
            this.items.splice(this.randomIndex(), 1)
        }
    }
})

运行结果:



这个例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,我们下面会解决这个问题。

列表的位移过渡

<transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的v-move 特性,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。
v-move对于设置过渡的切换时机和过渡曲线非常有用,你会看到如下的例子:

<div id="app2">
    <button @click="shuffle">Shuffle</button>
    <transition-group name="flip-list" tag="ul">
        <li v-for="item in items" :key="item">
            {{item}}
        </li>
    </transition-group>
</div>
.flip-list-move {
     transition: transform 1s;
}
var app2 = new Vue({
    el:'#app2',
    data:{
        items:[1,2,3,4,5,6,7,8,9]
    },
    methods:{
        shuffle:function () {
            this.items = _.shuffle(this.items)
        }
    }
})

这个例子需要添加以下引用

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>

运行结果:



这个看起来很神奇,内部的实现,Vue 使用了一个叫 FLIP 简单的动画队列
使用 transforms 将元素从之前的位置平滑过渡新的位置。
我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。

<div id="app3" class="demo">
    <button @click="shuffle">Shuffle</button>
    <button @click="add">Add</button>
    <button @click="remove">Remove</button>
    <transition-group name="list-complete" tag="p">
        <span v-for="item in items" :key="item" class="list-complete-item">
            {{item}}
        </span>
    </transition-group>
</div>
.list-complete-item{
    transition: all 1s;
    display: inline-block;
    margin-right: 10px;
}
.list-complete-enter, .list-complete-leave-to{
    opacity: 0;
    transform: translateY(30px);
}
.list-complete-leave-active{
    position: absolute;
}
var app3 = new Vue({
    el:'#app3',
    data:{
        items:[1,2,3,4,5,6,7,8,9],
        nextNum:10
    },
    methods:{
        shuffle:function () {
            this.items = _.shuffle(this.items)
        },
        randomIndex:function () {
            return Math.floor(Math.random() * this.items.length)
        },
        add:function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
        },
        remove:function () {
            this.items.splice(this.randomIndex(), 1)
        }
    }
})

运行结果:


列表的渐进过渡

通过 data 属性与 JavaScript 通信 ,就可以实现列表的渐进过渡:

<div id="app4">
    <input v-model="query">
    <transition-group
        name="staggered-fade"
        tag="ul"
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <li v-for="(item, index) in computedList"
            :key="item.msg"
            :data-index="index">
            {{item.msg}}
        </li>
    </transition-group>
</div>
var app4 = new Vue({
    el:'#app4',
    data:{
        query:'',
        list:[
            {msg:'Bruce Lee'},
            {msg:'Jackie Chan'},
            {msg:'Chuck Norris'},
            {msg:'Jet Li'},
            {msg:'Kung Furry'},
            {msg:'Chain Zhang'},
            {msg:'Iris Zhao'},
        ]
    },
    computed:{
        computedList:function () {
            var vm = this
            return this.list.filter(function (item) {
                return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
            })
        }
    },
    methods:{
        beforeEnter:function (el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
            }, delay)
        },
        leave:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:0, height:0}, {complete:done})
            }, delay)
        }
    }
})

上述js代码需要添加对Velocity引用:

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

运行结果如下:


可复用的过渡

过渡可以通过 Vue 的组件系统实现复用。要创建一个可复用过渡组件,你需要做的就是将<transition>或者 <transition-group>作为根组件,然后将任何子组件放置在其中就可以了。
下面的例子是将上一个列表渐进过渡的例子改为可复用的过渡的源码:

<div id="app5">
    <input v-model="query">
    <my-transition :query="query" :list="list">
        <li v-for="(item, index) in computedList"
            :key="item.msg"
            :data-index="index">
            {{item.msg}}
        </li>
    </my-transition>
</div>
Vue.component('my-transition', {
    template:`
    <transition-group
        name="staggered-fade"
        tag="ul"
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <slot></slot>
    </transition-group>`,
    props:['query', 'list'],
    methods:{
        beforeEnter:function (el) {
            el.style.opacity = 0
            el.style.height = 0
        },
        enter:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
            }, delay)
        },
        leave:function (el, done) {
            var delay = el.dataset.index * 150
            setTimeout(function () {
                Velocity(el, {opacity:0, height:0}, {complete:done})
            }, delay)
        }
    }
})

var app5 = new Vue({
    el:'#app5',
    data:{
        query:'',
        list:[
            {msg:'Bruce Lee'},
            {msg:'Jackie Chan'},
            {msg:'Chuck Norris'},
            {msg:'Jet Li'},
            {msg:'Kung Furry'},
            {msg:'Chain Zhang'},
            {msg:'Iris Zhao'},
        ]
    },
    computed:{
        computedList:function () {
            var vm = this
            return this.list.filter(function (item) {
                return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
            })
        }
    },
})

效果与上一个例子一致:



但是函数组件更适合完成这个任务。由于暂时还没有学到render函数,所以暂时先不实现render函数组件。后面学到的时候再做打算。

动态过渡

在 Vue 中即使是过渡也是数据驱动的!动态过渡最基本的例子是通过 name 特性来绑定动态值。

<transition v-bind:name="transitionName">
  <!-- ... -->
</transition>

当你想用 Vue 的过渡系统来定义的 CSS 过渡/动画 在不同过渡间切换会非常有用。
所有的过渡特性都是动态绑定。它不仅是简单的特性,通过事件的钩子函数方法,可以在获取到相应上下文数据。这意味着,可以根据组件的状态通过 JavaScript 过渡设置不同的过渡效果。

<div id="app6">
    Fade In:
    <input type="range" v-model="fadeInDuration" min="0" :max="maxFadeDuration">
    Fade Out:
    <input type="range" v-model="fadeOutDuration" min="0" :max="maxFadeDuration">
    <transition
        v-bind:css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave">
        <p v-if="show">hello chain</p>
    </transition>
    <button @click="stop = true">Stop it</button>
</div>
var app6 = new Vue({
    el: '#app6',
    data: {
        show: true,
        fadeInDuration: 1000,
        fadeOutDuration: 1000,
        maxFadeDuration: 1500,
        stop: false
    },
    mounted: function () {
        this.show = false
    },
    methods: {
        beforeEnter: function (el) {
            el.style.opacity = 0
        },
        enter: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 1 },
                {
                    duration: this.fadeInDuration,
                    complete: function () {
                        done()
                        if (!vm.stop) vm.show = false
                    }
                }
            )
        },
        leave: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 0 },
                {
                    duration: this.fadeOutDuration,
                    complete: function () {
                        done()
                        vm.show = true
                    }
                }
            )
        }
    }
})

运行结果:


其中例子里的mounted是在Vue挂载完成,也就是模板中的html渲染到html页面中时的一个钩子函数,只会执行一次。具体内容可以理解下Vue的生命周期,这里就不赘述了。
但是如果这里不使用mounted的话,也是可以用初始渲染来实现,只不过比较麻烦。实现的方法是:
在transition中加入appear钩子函数:@appear="appear",然后在vue实例的methods中添加appear方法:

        appear: function (el, done) {
            var vm = this
            Velocity(el,
                { opacity: 1 },
                {
                    duration: this.fadeInDuration,
                    complete: function () {
                        done()
                        if (!vm.stop) vm.show = false
                    }
                }
            )
        }

本文为原创,转载请注明出处
上一节:Vue学习笔记进阶篇——多元素及多组件过渡
返回目录
下一节:Vue学习笔记进阶篇——过渡状态

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容