1. 程式人生 > >【重點突破】—— Vue2.0 transition 動畫Demo實踐填坑

【重點突破】—— Vue2.0 transition 動畫Demo實踐填坑

前言:vue1.0版本和2.0版本的過渡系統改變是很大的,具體請詳看文件介紹本文轉載自郭錦榮的部落格,一共列舉了四種transition的使用實踐,分別是css過渡、css動畫、javascript鉤子、列表過渡的應用,這裡只作為學習梳理。


 

css過渡--實踐

 

 

Demo效果:

css過濾

 

實現思路:通過一個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 類名。

 

css 動畫--實踐

 

 

Demo效果

css動畫

這個案例的不同之處在於過渡效果是使用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>

 

實現思路:只需要在vue過渡類名下加了不同的animation

官網說明:當只使用transition或animation其中一種時,vue是能自動監聽對應的型別的,但是如果同一個元素同時使用兩種效果,就需要明確指定監聽哪一種型別

其實這個demo已經簡單地實現同時使用兩種型別的情況,可以看到有一個透明度的變化。

注意: 假如animation裡使用了transform,並且外面也使用了transform的話,那麼元素在過渡的時候動畫效果就會有衝突,效果就有點出入了。

 

JavaScript鉤子 -- 實踐

 

 

前兩個Demo都是有進入和離開的過渡,但是如果一些場景只需要進入過渡然後就結束了,那麼這時就可以使用JavaScript鉤子結合CSS transitions/animations來實現,當然也可以單獨使用。

 

Demo效果:

 

javascript鉤子

使用場景:這個一個非常low的模擬炮彈發射的場景,可以看到小球有拋物線軌跡運動的過渡,而且發射出去就不會再回來了     

關鍵程式碼:

<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>
</template>

 

誤區:由於本身這個案例是一組元素的過渡,所以很容易就會覺得用Vue2.0提供的transition-group不就行了。

糾正:transition-group是列表過渡,要使用的那一組元素是相關聯的、互相影響的

        而這個案例的元素每個都是獨立的,只不過是一組獨立的元素過渡,所以還是用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';
            }
        }
    };

 

  • 過渡元素就不需要為其新增vue的過渡css類名了,只需在元素本身新增transition即可,那vue在之前css過渡的時候會自動幫我們去新增對應的類名來完成過渡效果。
  • javascript鉤子需要我們自己完成這個始末狀態的設定
  • 當我們點選觸發一個過渡的時候,在beforeEnter裡先拿到當前元素的偏移位置,然後給過渡元素設定其起始位置,在enter裡需要重新觸發下瀏覽器的重繪,然後在下一幀重新設定元素的結束位置,這時就會產生過渡效果,在過渡完成後我們將當前元素隱藏即可。

 

transition-group -- 實踐

 

 


Demo效果:

transition-group.gif

使用場景:這個Demo是一個簡單的todo lists,當其中一個元素過渡的時候,會影響其他元素的過渡。當然,刪除按鈕其實本身也是一個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的時候將索引值index傳給key,結果過渡不對,換成對應的item就正常了。

這個Demo用到了vue-touch,雖然github上說不支援2.0版本了,但是有一個next分支是支援的,只需在專案下安裝它即可:

(sudo) npm install --save git://github.com/vuejs/vue-touch.git#next
#sudo  mac環境使用

 

關鍵樣式:

.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才會有效果。現在看來其實列表過渡也是很容易實現的。


注:轉載請註明出處