「造輪子」—Xue-ui (輪播)

在使用 Vue 之后,寫了兩個(gè)版本的輪播,一個(gè)版本利用 Vue 的動(dòng)畫很輕松地寫出了無(wú)縫輪播,但是由于 flextransform 一些錯(cuò)綜復(fù)雜的關(guān)系,導(dǎo)致圖片切換時(shí)圖片間會(huì)有縫隙,強(qiáng)迫癥受不了,于是寫了第二個(gè)版本利用克隆 dom 的方式實(shí)現(xiàn)無(wú)縫輪播。簡(jiǎn)單總結(jié)兩個(gè)版本輪播的實(shí)現(xiàn)思路。

第一個(gè)版本

這個(gè)版本是課程里實(shí)現(xiàn)輪播的思路,特點(diǎn)是充分利用 Vue 的動(dòng)畫,完全不操作 dom ,只操作數(shù)據(jù)。

組件基本結(jié)構(gòu)
<x-slides>
        <x-slides-item :index="0"><div class="img">1</div></x-slides-item>
        <x-slides-item :index="1"><div class="img">2</div></x-slides-item>
        <x-slides-item :index="2"><div class="img">3</div></x-slides-item>
        <x-slides-item :index="3"><div class="img">4</div></x-slides-item>
</x-slides>

組件分為容器組件和單個(gè) item 子組件,均帶有一個(gè)插槽。

容器組件

容器組件的關(guān)鍵 css 屬性:

overflow: hidden;
position: relative;
display: flex;

容器組件在 data 設(shè)置一個(gè)屬性 currentitem 子組件接受props

        props: {
            index: {
                type: Number,
                required: true
            }
        }

由于 index 是組件切換的依賴,因此在使用時(shí)是必須傳的。想過(guò)嘗試在子組件的 data 中聲明 index ,然后在父組件 mounted 鉤子中遍歷 this.$children 去設(shè)置每個(gè)子組件的 index ,這樣就可以不傳這個(gè) props 了。但是這種做法并不好,首先,Vue 并不保證 mounted 鉤子里子組件也一并掛載完畢,有可能會(huì)出現(xiàn)某個(gè)子組件未掛載完畢而沒(méi)有被設(shè)置 index 的問(wèn)題,要確保每個(gè)子組件都被遍歷到,需要使用 nextTick 鉤子;其次 this.$children 也不保證順序,可能會(huì)出現(xiàn)圖片順序混亂的問(wèn)題。
PS:另一種使用 nextTick 鉤子的方式:
通常我們使用 nextTick 是這樣,提供一個(gè)回調(diào)函數(shù),比如在 mounted 中使用:

        mounted() {
            this.$nextTick(() => {
                doSomething()
            })
        }

根據(jù)官方文檔,2.1.0 起,如果沒(méi)有提供回調(diào), nextTick 將返回一個(gè) promise ,因此上述代碼也可以這樣寫:

        async mounted() {
            await this.$nextTick()
            doSomething()
        }
子組件

子組件模板:

    <transition name="slide">
        <div class="x-slides-item" v-show="index===current" :class="{reverse}">
            <slot></slot>
        </div>
    </transition>

簡(jiǎn)單的通過(guò) index===current 來(lái)個(gè)控制子組件的顯示, index 是子組件接受的 props ,current 是子組件通過(guò)計(jì)算屬性映射的容器組件的 current

        computed: {
            current() {
                return this.$parent.current
            }
        }

這樣,容器通過(guò)改變 current 的值,就可以控制子組件的切換。

動(dòng)畫效果

此版本輪播主要就在于切換時(shí)的動(dòng)畫效果。
首先給子組件加上過(guò)渡效果,Vue 過(guò)渡類名對(duì)應(yīng) css 如下:

    .slide-leave-active {
        position: absolute;
        top: 0;
        left: 0;
    }
    .slide-enter {
        transform: translateX(100%);
    }
    .slide-leave-to {
        transform: translateX(-100%);
    }
    .slide-enter.reverse {
        transform: translateX(-100%);
    }
    .slide-leave-to.reverse {
        transform: translateX(100%);
    }

由于絕對(duì)定位會(huì)讓元素脫離文檔流,如果所有子組件根元素都使用絕對(duì)定位,那么容器組件將出現(xiàn)高度塌陷的問(wèn)題,于是采用只讓正在離開的子組件絕對(duì)定位的方式來(lái)保證容器組件的高度。

切換方向改變

通過(guò)在子組件中使用 watch ,可以判斷切換的方向,從而控制動(dòng)畫的方向

        watch: {
            current(val, oldVal) {
                if (val - oldVal > 0) {
                    this.reverse = false;
                }
                if (val - oldVal < 0) {
                    this.reverse = true;
                }
                //最后一個(gè)到第一個(gè)
                if (val - oldVal === -(this.len - 1)) {
                    this.reverse = false;
                }
                //第一個(gè)到最后一個(gè)
                if (val - oldVal === this.len - 1) {
                    this.reverse = true;
                }
            }
        }

當(dāng)切換方向改變時(shí),子組件 reverse 類名激活,其切換動(dòng)畫也會(huì)相應(yīng)改變。
這種方式實(shí)現(xiàn)輪播非常巧妙,契合 Vue 數(shù)據(jù)驅(qū)動(dòng)的思想,無(wú)任何 dom 操作。缺點(diǎn)是圖片切換時(shí)會(huì)有間隙,在嘗試多種方式無(wú)果后,采用克隆 dom 的方式實(shí)現(xiàn)了第二個(gè)版本的輪播。

第二個(gè)版本

組件基本結(jié)構(gòu)

第二個(gè)版本的輪播結(jié)構(gòu)更為簡(jiǎn)單

 <x-slides>
        <div class="img">1</div>
        <div class="img">2</div>
        <div class="img">3</div>
        <div class="img">4</div>
 </x-slides>

組件內(nèi)部會(huì)在掛載完畢之后,克隆第一張圖片放在最后一張圖片后,克隆最后一張圖片放在第一張圖片前。
克隆操作的代碼:

            cloneDom() {
                let nodes = this.$slots.default.filter(node => node.elm.nodeType !== 3)   
                nodes.forEach(node => {
                    node.elm.style['flex-shrink'] = 0
                })
                this.length=nodes.length
                const first = nodes[0].elm.cloneNode(true)
                const last = nodes[nodes.length - 1].elm.cloneNode(true)
                this.$refs.view.prepend(last)
                this.$refs.view.append(first)
            }

此處兩小坑,一是 Vue 單文件組件自動(dòng)取消空格,因此在單文件組件中遍歷插槽,其子元素個(gè)數(shù)和插入的圖片數(shù)量一致,然而如果是在 HTML 文件中使用組件,插槽中將出現(xiàn)文本節(jié)點(diǎn),因此在克隆操作前需要根據(jù) nodeType 屬性來(lái)過(guò)濾掉文本節(jié)點(diǎn);二是組件使用 flex 布局,會(huì)出現(xiàn)壓縮子元素的情況,因此在需設(shè)置子元素的 flex-shrink0。
此版本輪播的切換原理為通過(guò) translateX 屬性的改變來(lái)實(shí)現(xiàn),并且在第一張到最后一張和最后一張到第一張采用動(dòng)畫效果結(jié)束立即更換圖片的方式實(shí)現(xiàn),具體原理之前已經(jīng)用原生 JS 實(shí)現(xiàn)過(guò),不再贅述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明AI閱讀 16,171評(píng)論 3 119
  • 梅蘭竹菊是花中四君子,其中竹以它奮發(fā)向上的品格、虛心謙遜的品格、剛正不阿的氣節(jié)在中國(guó)文人的心中占據(jù)重要席位,在...
    惠風(fēng)和暢806閱讀 479評(píng)論 4 2
  • /細(xì)水長(zhǎng)流 又是安期誕,云山百草香。 微風(fēng)飄彩帶,細(xì)雨走梅樁。 鼓密朝天躍,鑼輕伏地藏。 除衣皆稚幼,少壯國(guó)家強(qiáng)。
    細(xì)水長(zhǎng)流瑜閱讀 358評(píng)論 0 6
  • 一、【時(shí)間】: 兩個(gè)月(20161227~20170227) 二、【目標(biāo)】: 融入公司,融入工作,積極向上,每天進(jìn)...
    夏苗苗閱讀 273評(píng)論 0 0

友情鏈接更多精彩內(nèi)容