vue2.0 transition -- 动画使用心得

来源:互联网 发布:惠东人民政府网络问政 编辑:程序博客网 时间:2024/05/17 07:46

实践

这里将通过四个实践小案例来体验和学习css过渡,css动画,javascript钩子,列表过渡的应用。至于案例用到的知识点就请自行学习官网文档。

1.css过渡–实践

先来看看demo效果:
这里写图片描述

这个案例其实很简单,通过一个transition来触发多个子元素的过渡效果,我们只需要定义元素对应的过渡效果就可以,其他事情vue会帮我们搞定,由此可以扩展出其他酷炫的过渡场景效果。先来看看这个简单案例的代码实现:

<template>    <div class="app">        <button @click="showMenu" class="btn">{{text}}</button>        <transition name="move">            <div class="menu" v-show="show">                <div class="inner inner-1">1</div>                <div class="inner inner-2">2</div>                <div class="inner inner-3">3</div>            </div>        </transition>    </div></template><script type="text/ecmascript-6">    export default {        data () {            return {                show: false            };        },        methods: {            showMenu () {                this.show = !this.show;            }        },        computed: {            text () {                return this.show ? '收' : '开';            }        }    };</script><style lang="stylus" rel="stylesheet/stylus">    .app        .btn            position: fixed            bottom: 50px            right: 10px            z-index: 10            width: 50px            height: 50px            line-height: 50px            border-radius: 50%            border: none            outline: none            color: #fff            font-size: 18px            background: blue        .menu            position: fixed            bottom: 50px            right: 10px            width: 50px            height: 50px            border-radius: 50%            transition: all .7s ease-in            &.move-enter-active                .inner                    transform: translate3d(0, 0, 0)                    transition-timing-function: cubic-bezier(0, .57, .44, 1.97)                .inner-1                    transition-delay: .1s                .inner-2                    transition-delay: .2s                .inner-3                    transition-delay: .3s            &.move-enter, &.move-leave-active                .inner                    transition-timing-function: ease-in-out                .inner-1                    transform: translate3d(0, 60px, 0)                    transition-delay: .3s                .inner-2                    transform: translate3d(40px, 40px, 0)                    transition-delay: .2s                .inner-3                    transform: translate3d(60px, 0, 0)                    transition-delay: .1s            .inner                display: inline-block                position: absolute                width: 30px                height: 30px                line-height: 30px                border-radius: 50%                background: red                text-align: center                color: #fff                transition: all .4s            .inner-1                top: -50px                left: 10px            .inner-2                left: -30px                top: -30px            .inner-3                left: -50px                top: 10px</style>

可以看到我们的代码基本主要是完成css过渡效果的样式,而触发过渡效果只是简单地通过一个click事件就搞定了,vue会自动嗅探目标元素是否有 CSS 过渡或动画,并在合适时添加/删除 CSS 类名。那下一个demo就来简单实现一下使用css animation 做过渡的效果。

2.css 动画–实践

先来看看demo效果:
这里写图片描述

这个案例其实跟上面的demo差不多,不同之处在于过渡效果是使用css动画来实现,看下实现的代码:

<template>    <div class="app">    <button @click="showball" class="btn">show</button>        <transition name="move" type="animation">            <div class="ball" v-show="show">                <div class="inner"></div>            </div>        </transition>    </div></template><script type="text/ecmascript-6">    export default {        data () {            return {                show: false            };        },        methods: {            showball () {                this.show = !this.show;            }        }    };</script><style lang="stylus" rel="stylesheet/stylus">    @keyframes shape-change {        0%, 100% {            border-radius: 50%            background: red        }        50% {            border-radius: 0            background: blue        }    }    @keyframes moveball-in {        0% {            transform: translate3d(300px,-200px,0)        }        50% {            transform: translate3d(100px,-400px,0)        }        100% {            transform: translate3d(0,0,0)        }    }    @keyframes moveball-out {        0% {            transform: translate3d(0,0,0)        }        50% {            transform: translate3d(100px,-400px,0)        }        100% {            transform: translate3d(300px,-200px,0)        }    }    .app        .btn            width: 40px            height: 30px            margin-top: 40px            border: none            outline: none            background: red            color: #fff        .ball            position: absolute            bottom: 20px            left: 20px            width: 50px            height: 50px            transition: all 1s cubic-bezier(.22,-0.86,.97,.58)            &.move-enter-active                opacity: 1                animation: moveball-in 1s                .inner                    animation: shape-change 1s            &.move-leave-active                opacity: 0.8                animation: moveball-out 1s                .inner                    animation: shape-change 1s            .inner                display: inline-block                width: 30px                height: 30px                border-radius: 50%                background: red                transition: all 1s linear</style>

从css代码可以看出,我们只是在vue过渡类名下加了不同的animation而已。官网文档明确说明当只使用transition或animation其中一种时,vue是能自动监听对应的类型的,但是如果同一个元素同时使用两种效果,就需要明确指定监听哪一种类型,不过官网并没有给出具体的栗子。那其实这个demo已经简单地实现同时使用两种类型的情况,可以看到有一个透明度的变化。但是假如animation里使用了transform,并且外面也使用了transform的话,那么元素在过渡的时候动画效果就会有冲突,效果就有点出入了。

3.JavaScript钩子 – 实践

前两个栗子都是有进入和离开的过渡,但是如果一些场景只需要进入过渡然后就结束了,那么这时就可以使用JavaScript钩子结合CSS transitions/animations来实现,当然也可以单独使用。看下demo:

这里写图片描述

这个一个非常low的模拟炮弹发射的场景,可以看到小球有抛物线轨迹运动的过渡,而且发射出去就不会再回来了,那么这个demo就是使用了JavaScript钩子结合css来实现的,接下来看下关键代码:

<template>    <div class="app">        <div class="gun" @click="launch($event)"></div>        <div class="shells-wrapper">          <transition v-for="shell in shells" name="launch-shell" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">            <div class="shell" v-show="shell.show">              <div class="inner"></div>            </div>          </transition>        </div>        <div class="goal"></div>        <!--小球第二种-->    <div class="ball-container">      <ul>        <li v-for="(ball,index) in balls" :key="index">          <transition :css="false" name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop">            <div class="ball" v-show="ball.show">              <div class="inner inner-hook"></div>            </div>          </transition>        </li>      </ul>    </div>    <!--小球 End-->    </div></template>

首先,由于本身这个demo是一组元素的过渡,所以有些童鞋就会觉得用2.0提供的transition-group不就行了嘛。不过transition-group是列表过渡,我的理解是那一组元素是相关联的、互相影响的,但是这个demo的元素每个都是独立的,只不过是一组独立的元素过渡,所以还是用transition比较合理,那使用v-for就可以实现一组相同过渡的元素啦。接下来看JavaScript钩子怎么实现这个过渡:

export default {        data () {            return {                shells: [                    {                        show: false                    },                    {                        show: false                    },                    {                        show: false                    }                ]            };        },        methods: {            launch (event) {                for (let i = 0; i < this.shells.length; i++) {                  let shell = this.shells[i];                  if (!shell.show) {                    shell.show = true;                    shell.target = event.target;                    return;                  }                }            },            beforeEnter (el) {                let count = this.shells.length;                while (count--) {                    let shell = this.shells[count];                    if (shell.show) {                        let rect = shell.target.getBoundingClientRect();                        let left = rect.left - 32;                        let top = -(window.innerHeight - rect.top - 15);                        el.style.display = '';                        el.style.webkitTransform = `translate3d(0,${top}px,0)`;                        el.style.transform = `translate3d(0,${top}px,0)`;                        let inner = el.getElementsByClassName('inner')[0];                        inner.style.webkitTransform = `translate3d(${left}px,0,0)`;                        inner.style.transform = `translate3d(${left}px,0,0)`;                    }                }            },            enter (el, done) {                /* eslint-disable no-unused-vars */                let refresh = el.offsetHeight;                this.$nextTick(() => {                    el.style.webkitTransform = 'translate3d(0,0,0)';                    el.style.transform = 'translate3d(0,0,0)';                    let inner = el.getElementsByClassName('inner')[0];                    inner.style.webkitTransform = 'translate3d(0,0,0)';                    inner.style.transform = 'translate3d(0,0,0)';                });                done();            },            afterEnter (el) {                let ball = this.shells[0];                ball.show = false;                el.style.display = 'none';            }        }    };

css样式代码:

.ball-container      .ball        position: absolute        left: 32px        bottom: 22px        z-index: 50        transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41)        .inner          width: 16px          height: 16px          border-radius: 50%          background: rgb(0, 160, 220)          transition: all 0.4s linear

过渡元素就不需要为其添加vue的过渡css类名了,只需在元素本身添加transition即可,那vue在之前css过渡的时候会自动帮我们去添加对应的类名来完成过渡效果,但是用javascript钩子就需要我们自己完成这个始末状态的设置了。当我们点击触发一个过渡的时候,我们在beforeEnter里先拿到当前元素的偏移位置,然后给过渡元素设置其起始位置,在enter里需要重新触发下浏览器的重绘,然后在下一帧重新设置元素的结束位置,这时就会产生过渡效果,在过渡完成后我们将当前元素隐藏即可。那刚才讲到的列表过渡,接下来就是关于使用transition-group的一个小demo了。

4.transition-group – 实践

先看下demo效果:
这里写图片描述

其实就是个简单的todo lists的小demo,可以看到,当其中一个元素过渡的时候,会影响其他元素的过渡。当然,删除按钮其实本身也是一个transition过渡,也就是说可以在transition-group里使用transition,看下相关代码:

<template>    <div class="app">        <button @click="add" class="add-btn">+</button>        <transition-group name="slide" tag="ul" class="list-wrapper">            <li class="list" v-for="(item, index) in lists" v-touch:swipeleft="showBtn.bind(this, index)" v-touch:swiperight="hideBtn.bind(this, index)" :key="item">                <span class="text">{{item.text}}</span>                <transition name="move">                    <button class="del-btn" @click="delList(index)" v-show="item.show">删除</button>                </transition>            </li>        </transition-group>    </div></template>

有个小坑的地方就是,之前看官网列表过渡的栗子,它是一个数组,元素都是数字,并且每一项都必须设置唯一的key值。所以我完成demo的时候就自作聪明地将索引值传给key,结果过渡老是不对,后来换成对应的item就正常了(生无可恋脸)。这个demo用到了vue-touch,虽然github上说不支持2.0版本了,但是有一个next分支是支持的,只需在项目下安装它即可:

sudo npm install --save git://github.com/vuejs/vue-touch.git#next

这里看下主要的样式:

.list    display: flex    width: 100%    height: 40px    line-height: 40px    margin-bottom: 10px    color: #666    font-size: 14px    background: #eee    transition: all .4s    &.slide-move        transition: transform 1s    &.slide-enter        transform: translate3d(-100%, 0, 0)    &.slide-leave-active        position: absolute        transform: translate3d(-100%, 0, 0)    &:last-child        margin-bottom: 0    .del-btn        flex: 0 0 60px        border: none        outline: none        color: #fff        background: red        transition: all .4s        &.move-enter, &.move-leave-active            transform: translate3d(70px, 0, 0)    .text        flex: 1        padding-left: 20px

如果改变定位过渡的duration与进入离开一样的话,其实可以不用-move,这里设置-move的过渡的duration不同于元素进入离开的duration产生一种速度差,看起来舒服点。而且-leave-active需要设置position: absolute才会有效果。现在看来其实列表过渡也是很容易实现的。

记录一下vue的动画案例

原创粉丝点击