vue組件開發練習--焦點圖切換

1.前言

vue用了有一段時間了,開發的後臺管理系統也趨於完善,如今時間比較算是有點空閒吧!這個空閒時間我在研究vue的另外的一些玩法,好比組件,插件等。今天,我就分享一個組件的練手項目--焦點圖切換組件。這個項目是我用於vue組件練習的一個項目,固然了,代碼也會提交到github(ec-slider),有空也會維護。我也想我開發的東西好用一點!如今,就是建議有須要的夥伴,能夠來玩下這個項目,當練習的做用!另外,若是你們有什麼建議,歡迎指點!javascript

建議
1.下面的步驟,最好在本身本地上跑起來,根據文章的步驟,逐步完成,若是隻看代碼,很容易懵逼的。
2.若是不清楚哪一個代碼有什麼做用,可能本身調試下,把代碼去掉後,看下有什麼影響,就很容易想出代碼有什麼做用了!

2.項目目錄

clipboard.png

很普通,很好理解的一個目錄,但仍是簡單的解釋一下吧css

node_modules:文件依賴模塊(自動生成)
dist:打包文件產出目錄(自動生成)
src:開發文件目錄
src/components:組件文件目錄
.babelrc:babel編譯es6的配置文件
.gitnore:不提交到git的文件(目錄)的配置文件
fontSize:設置rem算法的文件(如今沒用到,忽略)
index.html:模板文件
index.js:入口文件
package.json:配置文件
README.md:說明文檔
webpack.config.babel.js:webpack配置文件html

3.步驟詳解

3-1跑起來

這是項目的第一步(項目搭建這個,我很少說,以前的文章已經說了幾回了!),如今src/components/ec-slider.vue這裏輸出一個‘守候’
1.首先,在src/components/ec-slider.vue裏面輸出‘守候’,代碼以下vue

<template>
    <div>
        守候
    </div>
</template>
<script type="text/javascript">
    export default {
        data () {
            return {

            }
        },
        computed: {

        },
        mounted(){

        },
        props: [],
        methods: {

        }
    }
</script>

2.而後,在src/components/index.js裏面設置註冊組件(要帶一個install方法),代碼以下java

import SlideImg from './ec-slider.vue'
const ecslide={
    install:function (Vue) {
        Vue.component('ec-slide',SlideImg)
    }
}
export default ecslide;

3.在入口文件,index.js裏面引入而且使用組件node

require("./index.html");
require("./src/sass/index.scss");
import Vue from 'vue'
//引入而且使用組件
import ecslide from './src/js/components/index';
Vue.use(ecslide);
let app6 = new Vue({
    el: "#app6",
    data: {

    },
    mounted(){
        
    }

});

4.在index.html(模板文件),輸出組件webpack

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <title>Title</title>
    </head>
    <body>
    <div id="app6">
        <ec-slide></ec-slide>
    </div>
    </body>
</html>

5.命令行輸入$ npm run dev跑起來,結果完美!這幾步的原理貌似沒什麼可多說的,都是固定式的步驟。git

clipboard.png

3-2開發準備

通過上一步以後,基礎就已經打好了,那麼接下來就是一個開發的過程,大部分都是修改src/components/ec-slider.vue這個文件。
開發以前,你們不要急着寫代碼,先分析下當中的運行流程!
首先,一個焦點圖切換,須要什麼參數?根據下面的一個淘寶栗子,我簡單分析下,就是下面這幾個!
clipboard.pnges6

list-圖片列表[{src:'url',href:'https://www.baidu.com'},{src:'url',href:'http://www.163.com'}](src:圖片的src,href:跳轉鏈接,點擊圖片的時候)
autoplay-是否自動播放 布爾 (默認false)
type-輪播方式‘transparent’(透明度切換), 'slide'(滑動切換) (默認slide)
option-對應切換 (默認false,不顯示)
time-輪播間隔時間,毫秒 (默認4000)
sildetype-過渡效果 (默認'ease'慢速開始,而後變快,而後慢速結束的過渡效果,參考:transition-timing-function
arrowurl-箭頭圖片連接
arrowsize-箭頭尺寸‘width,height’
direction-切換方向'left'(左右) 'top'(上下) (默認:左右)github

分析完了以後,就知道暫時須要這麼多參數,那麼接下來就是在ec-slider.vue裏面,接收這些參數。父子組件傳參方式,我想你們知道--props。代碼以下

<template>
    <div>
        守候
    </div>
</template>
<script type="text/javascript">
    export default {
        data () {
            return {

            }
        },
        computed: {

        },
        mounted(){

        },
        props: ['list', 'autoplay', 'type', 'time', 'sildetype', 'arrowurl','arrowsize','option','direction'],
        methods: {

        }
    }
</script>

有地方接收參數,確定要有地方傳參數,就是index.html模板文件裏面傳

<div class="slider-left">
    <ec-slide :list='list' :autoplay="true" :type="'slide'" :option="true" :time="4000" :sildetype="'ease'" :arrowurl="'http://i1.buimg.com/1949/4d860a3067fab23b.jpg'" :arrowsize="'20,40'" :direction="'left'"></ec-slide>
</div>

3-3樣式佈局

既然知道了,會接收什麼參數,那下面先把樣式佈局,給弄好先,這個很少說,代碼以下!(有些解釋我也是直接打到代碼上)

<template>
    <div class="switch-img-box" id="ec-slide-box">
        <div class="switch-img-type switch-img-left">
            <ul :style="{'width':ulWidth,'transition-timing-function':slideChange}">
                <li v-for="(li,index) in list" :style="{'width':listWidth+'%'}">
                    <a :href="li.href?li.href:'javascript:;'">
                        <img :src="li.src" class="slider-img"/>
                    </a>
                </li>
            </ul>
        </div>
        <!--若是須要顯示對應的點-->
        <div class="switch-option" v-if="option">
            <div>
                <span v-for="(li,index) in list"></span>
            </div>
        </div>
        <!--若是須要顯示箭頭-->
        <div class="switch-arrow" v-if="arrowurl&&arrowsize">
            <div :class="{'arrow-left':direction==='left','arrow-top':direction==='top'}"
                 :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}"></div>
            <div :class="{'arrow-right':direction==='left','arrow-bottom':direction==='top'}"
                 :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}"></div>
        </div>
    </div>
</template>
<script type="text/javascript">
    export default {
        data () {
            return {
                slideChange: '',
                arrowWidth: '',
                arrowHeight: '',
            }
        },
        computed: {
            //ul寬度
            ulWidth: function () {
                return (this.list.length) + "00%";

            },
            //li寬度
            listWidth: function () {
                return 100 / (this.list.length)
            }
        },
        mounted(){
            //設置各個數據初始值
            this.slideChange = this.sildetype || 'ease';
            if (this.arrowsize && this.arrowurl) {
                this.arrowWidth = this.arrowsize.split(',')[0];
                this.arrowHeight = this.arrowsize.split(',')[1];
            }
        },
        props: ['list', 'autoplay', 'type', 'time', 'sildetype', 'arrowurl', 'arrowsize', 'option', 'direction'],
        methods: {
        }
    }
</script>
<style lang="scss">
    .ec-slide-img-box {
        width: 100%;
        height: 100%;
        position: relative;
        touch-action: none;
    }

    .ec-slide-img-type {
        position: relative;
        overflow: hidden;
        width: 100%;
        height: 100%;
        &.ec-slide-img-top {
        }
        &.ec-slide-img-left {
            li {
                display: inline-block;
                font-size: 0;
            }
        }
        &.ec-slide-img-transparent {
            li {
                opacity: 0;
                transition: opacity 1s;
                width: 0;
                &.cur {
                    width: auto;
                }
                &.show {
                    opacity: 1;
                }
            }
        }
        ul {
            font-size: 0;
            &.tran {
                transition: all .4s;
            }
            li {
                text-align: center;
            }

            img {
                vertical-align: middle;
                max-width: 100%;
                max-height: 100%;
            }
        }
    }

    .ec-slide-arrow {
        div {
            position: absolute;
            z-index: 2;
            margin: auto;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            opacity: .5;
            &:hover {
                opacity: 1;
            }
            &.arrow-left {
                left: 10px;
                right: auto;
            }
            &.arrow-right {
                right: 10px;
                left: auto;
                transform: rotate(180deg);
            }
            &.arrow-top {
                top: 10px;
                bottom: auto;
            }
            &.arrow-bottom {
                bottom: 10px;
                top: auto;
                transform: rotate(180deg);
            }
        }
    }

    .ec-slide-option {
        position: absolute;
        font-size: 0;
        bottom: 10px;
        text-align: center;
        width: 100%;
        z-index: 5;
        &.isFirst {
            span:first-child {
                display: none;
            }
        }
        &.isLast {
            span:last-child {
                display: none;
            }
        }
        span {
            border-radius: 100%;
            margin: 0 5px;
            background: #fff;
            display: inline-block;
            width: 10px;
            height: 10px;
            &.active {
                background: #09f;
            }
        }
        &.ec-slide-option-top {
            display: table;
            width: 10px;
            height: 100%;
            top: 0;
            right: 10px;
            margin: auto;
            bottom: 0;
            span {
                margin: 5px 0;
            }
            div {
                display: table-cell;
                vertical-align: middle;
            }
        }
    }
</style>

運行結果,就是下面這樣

clipboard.png

3-4執行動畫

佈局搞定了,下面就能夠寫動畫,讓輪播動起來!這裏也須要增長几個變量,一個是nowIndex,記錄當前索引。一個是timer定時器!
首先,我用transform:translate3d()這個方式控制ul的滑動。

<ul :style="{'width':ulWidth,'transform':'translate3d(-'+(listWidth*(nowIndex))+'%,0,0)','transition-timing-function':slideChange,'transition': 'all .4s'}">
    <li v-for="(li,index) in list" :style="{'width':listWidth+'%'}">
        <a :href="li.href?li.href:'javascript:;'">
            <img :src="li.src" class="slider-img"/>
        </a>
    </li>
</ul>

而後,根據nowIndex,設置對應點的class。

<div class="switch-option" v-if="option">
    <div>
        <!--若是當前索引index等於nowIndex。則添加active這個class,點就會變成藍色-->
        <span v-for="(li,index) in list" :class="{'active':index===nowIndex}"></span>
    </div>
</div>

js代碼以下

<script type="text/javascript">
    export default {
        data () {
            return {
                nowIndex: 0,
                timer: null,
                slideChange: '',
                arrowWidth: '',
                arrowHeight: '',
            }
        },
        computed: {
            //ul寬度
            ulWidth: function () {
                return (this.list.length) + "00%";

            },
            //li寬度
            listWidth: function () {
                return 100 / (this.list.length)
            }
        },
        mounted(){
            //是否自動播放
            if (this.autoplay) {
                this.autoSwitch();
            }
            //設置初始值
            this.slideChange = this.sildetype || 'ease';
            if (this.arrowsize && this.arrowurl) {
                this.arrowWidth = this.arrowsize.split(',')[0];
                this.arrowHeight = this.arrowsize.split(',')[1];
            }
        },
        props: ['list', 'autoplay', 'type', 'time', 'sildetype', 'arrowurl', 'arrowsize', 'option', 'direction'],
        methods: {
            //滑動操做
            switchDo(reduce){
                clearInterval(this.timer);
                //根據reduce判斷this.nowIndex的增長或者減小!
                //若是是減小模式reduce=‘reduce’
                if (reduce === 'reduce') {
                    //若是nowIndex等於0,已是第一個了,就回到最後一個
                    if (this.nowIndex === 0) {
                        this.nowIndex = this.list.length - 1;
                    }
                    else {
                        this.nowIndex--;
                    }
                }
                //若是是增長模式reduce=undefined
                else {
                    //若是nowIndex等於this.list.length-1,已是最後一個了,就回到第一個
                    if (this.nowIndex === this.list.length-1) {
                        this.nowIndex = 0;
                    }
                    else{
                        this.nowIndex++;
                    }
                }
                //若是須要自動播放
                if (this.autoplay) {
                    this.autoSwitch();
                }

            },
            //自動播放函數
            autoSwitch(){
                let time = this.time || 4000;
                this.timer = setInterval(() => {
                    this.switchDo();
                }, time);
            }
        }
    }
</script>

到了這裏,剩下的就只有點擊兩個箭頭,執行相應動畫,這個就相對簡單,無非就是調用switchDo函數,惟一區別在於,點擊左邊的箭頭,是減小模式,右邊箭頭的增長模式。代碼以下,很好理解。

<!--判斷是否須要顯示箭頭-->
<div class="switch-arrow" v-if="arrowurl&&arrowsize">
    <div :class="{'arrow-left':direction==='left','arrow-top':direction==='top'}"
         :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}" @click.stop="switchDo('reduce')"></div>
    <div :class="{'arrow-right':direction==='left','arrow-bottom':direction==='top'}"
         :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}" @click.stop="switchDo"></div>
</div>

到了這裏,對交互有強迫症的開發者就受不了了,到了最後一張,再點擊右邊箭頭,就會出現下面的狀況!

clipboard.png

到了第一張,再點擊左邊箭頭也是相似的狀況,這樣就很很差。理想狀況是下面這樣

clipboard.png

3-5細節優化

要想作上面的效果,改的地方會比較多,先說下原理吧,到了最後一張,這個時候,再點擊右邊箭頭,像淘寶那樣,回到第一張。到了第一張,再點擊左邊箭頭相似效果回到最後一張。那麼最後的佈局是這樣

clipboard.png

這樣佈局能實現效果,到了最後一張,這個時候,再點擊右邊箭頭,像淘寶那樣,回到第一張。就像下面

clipboard.png

這個時候,就須要多作一步,滾動到這裏的時候,瞬間拉回去。並且這個拉回去,要把ul的過渡效果transition去掉,否則就會看到拉回去的過渡效果!同時要改變nowIndex。

clipboard.png

1.首先,ul佈局方面

<div class="switch-img-type switch-img-left" v-if="type==='slide'&&direction==='left'">
    <!--用tran這個class控制ul是否含有過渡效果,樣式已經寫好-->
    <ul :style="{'width':ulWidth,'transform':'translate3d(-'+(listWidth*(nowIndex+1))+'%,0,0)','transition-timing-function':slideChange}"
        :class="{'tran':noLast}">
        <!--最後一張圖片-->
        <li :style="{'width':listWidth+'%'}">
            <a :href="list[list.length-1].href?list[list.length-1].href:'javascript:;'">
                <img :src="list[list.length-1].src" class="slider-img"/>
            </a>
        </li>
        <!--遍歷出來的圖片-->
        <li v-for="(li,index) in list" :style="{'width':listWidth+'%'}">
            <a :href="li.href?li.href:'javascript:;'">
                <img :src="li.src" class="slider-img"/>
            </a>
        </li>
        <!--第一張圖片-->
        <li :style="{'width':listWidth+'%'}">
            <a :href="list[0].href?list[0].href:'javascript:;'">
                <img :src="list[0].src" class="slider-img"/>
            </a>
        </li>
    </ul>
</div>

2.而後,對應的點修改

<!--isLast:隱藏最後一個span,isFirst隱藏第一個span-->
<div class="switch-option" v-if="option"
     :class="{'isLast':nowIndex===list.length, 'isFirst':nowIndex===-1,'switch-option-top':direction==='top'}">
    <div>
        <span class="active span1" v-if="nowIndex===list.length"></span>
        <span v-for="(li,index) in list" :class="{'active':index===nowIndex}"></span>
        <span class="active span2" v-if="nowIndex===-1"></span>
    </div>
</div>

這個可能會有點繞,我解釋下,好比滾動最後一張了,再點擊右邊箭頭,向右滑動到第一張的時候,以下圖

clipboard.png

這個時候又要把第一個點變成藍色,可是對應點的索引和nowIndex對不上,這個時候用一個技巧。把第一個(.span1)點顯示出來,而後把最後一個點隱藏。這樣仍是用戶看到仍是看到4個點在屏幕!等動畫執行完了,拉回去第一張的時候。把.span1隱藏,正常顯示對應的點!這個你們細想一下就知道了。到了第一張,再點擊左邊箭頭相似效果回到最後一張也是相同的處理方式!

clipboard.png

到這裏,功能就基本完成了,下面給出這部分代碼!

<template>
    <div class="ec-slide-img-box" id="ec-slide-box">
        <div class="ec-slide-img-type ec-slide-img-left" v-if="type==='slide'&&direction==='left'">
            <!--用tran這個class控制ul是否含有過渡效果,樣式已經寫好-->
            <ul :style="{'width':ulWidth,'transform':'translate3d(-'+(listWidth*(nowIndex+1))+'%,0,0)','transition-timing-function':slideChange}"
                :class="{'tran':noLast}">
                <!--最後一張圖片-->
                <li :style="{'width':listWidth+'%'}">
                    <a :href="list[list.length-1].href?list[list.length-1].href:'javascript:;'">
                        <img :src="list[list.length-1].src" class="slider-img"/>
                    </a>
                </li>
                <!--遍歷出來的圖片-->
                <li v-for="(li,index) in list" :style="{'width':listWidth+'%'}">
                    <a :href="li.href?li.href:'javascript:;'">
                        <img :src="li.src" class="slider-img"/>
                    </a>
                </li>
                <!--第一張圖片-->
                <li :style="{'width':listWidth+'%'}">
                    <a :href="list[0].href?list[0].href:'javascript:;'">
                        <img :src="list[0].src" class="slider-img"/>
                    </a>
                </li>
            </ul>
        </div>
        <!--isLast:隱藏最後一個span,isFirst隱藏第一個span-->
        <div class="ec-slide-option" v-if="option"
             :class="{'isLast':nowIndex===list.length, 'isFirst':nowIndex===-1,'ec-slide-option-top':direction==='top'}">
            <div>
                <span class="active" v-if="nowIndex===list.length"></span>
                <span v-for="(li,index) in list" :class="{'active':index===nowIndex}"></span>
                <span class="active" v-if="nowIndex===-1"></span>
            </div>
        </div>
        <div class="ec-slide-arrow" v-if="arrowurl&&arrowsize">
            <div :class="{'arrow-left':direction==='left','arrow-top':direction==='top'}"
                 :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}"
                 @click.stop="switchDo('reduce')"></div>
            <div :class="{'arrow-right':direction==='left','arrow-bottom':direction==='top'}"
                 :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}"
                 @click.stop="switchDo"></div>
        </div>
    </div>
</template>
<script type="text/javascript">
    export default {
        data () {
            return {
                nowIndex: 0,
                noLast: true,
                timer: null,
                slideChange: '',
                arrowWidth: '',
                arrowHeight: ''
            }
        },
        computed: {
            ulWidth: function () {
                return (this.list.length + 2) + "00%";

            },
            listWidth: function () {
                return 100 / (this.list.length + 2)
            }
        },
        mounted(){
            if (this.autoplay) {
                this.autoSwitch();
            }
            this.slideChange = this.sildetype || 'ease';
            if (this.arrowsize && this.arrowurl) {
                this.arrowWidth = this.arrowsize.split(',')[0];
                this.arrowHeight = this.arrowsize.split(',')[1];
            }
        },
        props: ['list', 'autoplay', 'type', 'time', 'sildetype', 'arrowurl', 'arrowsize', 'option', 'direction'],
        methods: {
            //滑動操做
            switchDo(reduce){
                clearInterval(this.timer);
                //根據reduce判斷this.nowIndex的增長或者減小!
                if (reduce === 'reduce') {
                    if (this.nowIndex === 0) {
                        //若是是滑動切換
                        this.nowIndex--;
                        //執行完了此次動畫以後,去除過渡效果
                        setTimeout(() => {
                            this.nowIndex = this.list.length - 1;
                            this.noLast = false;
                        }, 400)
                    }
                    else {
                        this.nowIndex--;
                    }
                }
                else {
                    this.nowIndex++;
                }
                if (this.nowIndex === this.list.length) {

                    //執行完了此次動畫以後,去除過渡效果
                    setTimeout(() => {
                        this.nowIndex = 0;
                        this.noLast = false;
                    }, 400)

                }
                //若是須要自動播放
                if (this.autoplay) {
                    this.autoSwitch();
                }
                //若是是滑動切換,設置this.noLast,增長過渡效果
                this.noLast = true;

            },
            //自動播放函數
            autoSwitch(){
                let time = this.time || 4000;
                this.timer = setInterval(() => {
                    this.switchDo();
                }, time);
            }
        }
    }
</script>
<style lang="scss">
    .ec-slide-img-box {
        width: 100%;
        height: 100%;
        position: relative;
        touch-action: none;
    }

    .ec-slide-img-type {
        position: relative;
        overflow: hidden;
        width: 100%;
        height: 100%;
        &.ec-slide-img-top {
        }
        &.ec-slide-img-left {
            li {
                display: inline-block;
                font-size: 0;
            }
        }
        &.ec-slide-img-transparent {
            li {
                opacity: 0;
                transition: opacity 1s;
                width: 0;
                &.cur {
                    width: auto;
                }
                &.show {
                    opacity: 1;
                }
            }
        }
        ul {
            font-size: 0;
            &.tran {
                transition: all .4s;
            }
            li {
                text-align: center;
            }

            img {
                vertical-align: middle;
                max-width: 100%;
                max-height: 100%;
            }
        }
    }

    .ec-slide-arrow {
        div {
            position: absolute;
            z-index: 2;
            margin: auto;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            opacity: .5;
            &:hover {
                opacity: 1;
            }
            &.arrow-left {
                left: 10px;
                right: auto;
            }
            &.arrow-right {
                right: 10px;
                left: auto;
                transform: rotate(180deg);
            }
            &.arrow-top {
                top: 10px;
                bottom: auto;
            }
            &.arrow-bottom {
                bottom: 10px;
                top: auto;
                transform: rotate(180deg);
            }
        }
    }

    .ec-slide-option {
        position: absolute;
        font-size: 0;
        bottom: 10px;
        text-align: center;
        width: 100%;
        z-index: 5;
        &.isFirst {
            span:first-child {
                display: none;
            }
        }
        &.isLast {
            span:last-child {
                display: none;
            }
        }
        span {
            border-radius: 100%;
            margin: 0 5px;
            background: #fff;
            display: inline-block;
            width: 10px;
            height: 10px;
            &.active {
                background: #09f;
            }
        }
        &.ec-slide-option-top {
            display: table;
            width: 10px;
            height: 100%;
            top: 0;
            right: 10px;
            margin: auto;
            bottom: 0;
            span {
                margin: 5px 0;
            }
            div {
                display: table-cell;
                vertical-align: middle;
            }
        }
    }
</style>

3-6其它切換方式

碼農怎麼會知足於現狀,只有一種切換方式,怎麼行,因此我又完善了些,1.一個透明度的切換方式。2.當傳進的list長度爲1的時候只顯示圖片,不進行任何動畫。3.左右滑動事件的處理(不規範處理)!雖然也是不多功能,可是我在平常開發能夠知足!
完整代碼以下,你們也能夠去github上面看代碼ec-slider

<template>
    <div class="ec-slide-img-box" id="ec-slide-box">
        <!--只有一張圖片的時候,只顯示,不作任何操做-->
        <div class="ec-slide-img" v-if="list.length===1">
            <a :href="list[0].href?list[0].href:'javascript:;'">
                <img :src="list[0].src"/>
            </a>
        </div>
        <!--左右滑動方式-->
        <div class="ec-slide-img-type ec-slide-img-left" v-if="type==='slide'&&direction==='left'&&list.length>1">
            <!--用tran這個class控制ul是否含有過渡效果,樣式已經寫好-->
            <ul :style="{'width':ulWidth,'transform':'translate3d(-'+(listWidth*(nowIndex+1))+'%,0,0)','transition-timing-function':slideChange}"
                :class="{'tran':noLast}" @touchstart="touchStar" @touchend="touchEnd">
                <!--最後一張圖片-->
                <li :style="{'width':listWidth+'%'}">
                    <a :href="list[list.length-1].href?list[list.length-1].href:'javascript:;'">
                        <img :src="list[list.length-1].src" class="slider-img"/>
                    </a>
                </li>
                <!--遍歷出來的圖片-->
                <li v-for="(li,index) in list":style="{'width':listWidth+'%'}">
                    <a :href="li.href?li.href:'javascript:;'">
                        <img :src="li.src" class="slider-img"/>
                    </a>
                </li>
                <!--第一張圖片-->
                <li :style="{'width':listWidth+'%'}">
                    <a :href="list[0].href?list[0].href:'javascript:;'">
                        <img :src="list[0].src" class="slider-img"/>
                    </a>
                </li>
            </ul>
        </div>
        <!--上下滑動方式-->
        <div class="ec-slide-img-type ec-slide-img-top" v-if="type==='slide'&&direction==='top'&&list.length>1" :style="{'height':boxHeight}">
            <ul :style="{'transform':'translate3d(0,-'+(listWidth*(nowIndex+1))+'%,0)','transition-timing-function':slideChange}"
                :class="{'tran':noLast}" @touchstart="touchStar" @touchend="touchEnd">
                <li>
                    <a :href="list[list.length-1].href?list[list.length-1].href:'javascript:;'">
                        <img :src="list[list.length-1].src" class="slider-img" @load="imgLoad"/>
                    </a>
                </li>
                <li v-for="(li,index) in list">
                    <a :href="li.href?li.href:'javascript:;'">
                        <img :src="li.src" class="slider-img" @load="imgLoad"/>
                    </a>
                </li>
                <li>
                    <a :href="list[0].href?list[0].href:'javascript:;'">
                        <img :src="list[0].src" class="slider-img" @load="imgLoad"/>
                    </a>
                </li>
            </ul>
        </div>
        <!--透明度滑動方式-->
        <div class="ec-slide-img-type ec-slide-img-transparent" v-if="type==='transparent'&&list.length>1">
            <ul @touchstart="touchStar" @touchend="touchEnd">
                <li v-for="(li,index) in list" :class="{'cur':index===nowIndex,'show':index===nowIndexShow}">
                    <a :href="li.href?li.href:'javascript:;'">
                        <img :src="li.src" class="slider-img"/>
                    </a>
                </li>
            </ul>
        </div>
        <!--isLast:隱藏最後一個span,isFirst隱藏第一個span-->
        <div class="ec-slide-option" v-if="option&&list.length>1" :class="{'isLast':nowIndex===list.length, 'isFirst':nowIndex===-1,'ec-slide-option-top':direction==='top'}">
            <div>
                <span class="active span1" v-if="nowIndex===list.length"></span>
                <span v-for="(li,index) in list" :class="{'active':index===nowIndex}"></span>
                <span class="active span2" v-if="nowIndex===-1"></span>
            </div>
        </div>
        <div class="ec-slide-arrow" v-if="arrowurl&&arrowsize&&list.length>1">
            <div :class="{'arrow-left':direction==='left','arrow-top':direction==='top'}" :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}" @click.stop="switchDo('reduce')"></div>
            <div :class="{'arrow-right':direction==='left','arrow-bottom':direction==='top'}" :style="{'width':arrowWidth+'px','height':arrowHeight+'px','background':'url('+arrowurl+') no-repeat','background-size':'100%'}"  @click.stop="switchDo"></div>
        </div>
    </div>
</template>
<script type="text/javascript">
    export default {
        data () {
            return {
                nowIndex: 0,
                nowIndexShow:0,
                noLast: true,
                timer: null,
                slideChange: '',
                arrowWidth:'',
                arrowHeight:'',
                startX:0,
                startY:0,
                boxHeight:0
            }
        },
        computed: {
            ulWidth: function () {
                return (this.list.length + 2) + "00%";

            },
            listWidth:function () {
                return 100/(this.list.length+2)
            }
        },
        mounted(){
            if (this.autoplay) {
                this.autoSwitch();
            }
            this.slideChange = this.sildetype || 'ease';
            if(this.arrowsize&&this.arrowurl){
                this.arrowWidth=this.arrowsize.split(',')[0];
                this.arrowHeight=this.arrowsize.split(',')[1];
            }
        },
        props: ['list', 'autoplay', 'type', 'time', 'sildetype', 'arrowurl','arrowsize','option','direction'],
        methods: {
            //開始滑動
            touchStar(e){
                //e.preventDefault();
                this.startX=e.changedTouches[0].clientX;
                this.startY=e.changedTouches[0].clientY;
            },
            //滑動結束
            touchEnd(e){
                //e.preventDefault();
                if(this.direction==='left'){
                    if(e.changedTouches[0].clientX-this.startX>50){
                        this.switchDo('reduce')
                    }
                    else if(e.changedTouches[0].clientX-this.startX<-50){
                        this.switchDo()
                    }
                }
                else if(this.direction==='top'){
                    if(e.changedTouches[0].clientY-this.startY>50){
                        this.switchDo('reduce')
                    }
                    else if(e.changedTouches[0].clientY-this.startY<-50){
                        this.switchDo()
                    }
                }
            },
            //滑動操做
            switchDo(reduce){
                clearInterval(this.timer);
                //根據reduce判斷this.nowIndex的增長或者減小!
                if(reduce==='reduce'){
                    if(this.nowIndex===0){
                        //若是是滑動切換
                        if(this.type==='slide'){
                            this.nowIndex--;
                            //執行完了此次動畫以後,去除過渡效果
                            setTimeout(() => {
                                this.nowIndex = this.list.length-1;
                                this.noLast = false;
                            }, 400)
                        }
                        else{
                            this.nowIndex = this.list.length-1;
                        }
                    }
                    else{
                        this.nowIndex--;
                    }
                }
                else{
                    this.nowIndex++;
                }
                if (this.nowIndex === this.list.length) {
                    if(this.type==='slide') {
                        //執行完了此次動畫以後,去除過渡效果
                        setTimeout(() => {
                            this.nowIndex = 0;
                            this.noLast = false;
                        }, 400)
                    }
                    else{
                        this.nowIndex = 0;
                    }
                }
                //是否顯示圖片,只針對透明度切換的狀況!
                setTimeout(()=>{
                    this.nowIndexShow=this.nowIndex;
                },1)
                //若是須要自動播放
                if (this.autoplay) {
                    this.autoSwitch();
                }
                //若是是滑動切換,設置this.noLast,增長過渡效果
                if(this.type==='slide') {
                    this.noLast = true;
                }

            },
            //自動播放函數
            autoSwitch(){
                let time = this.time || 4000;
                this.timer = setInterval(() => {
                    this.switchDo();
                }, time);
            },
            //獲取最大的高度,針對上下方向,滑動切換方式的處理
            imgLoad(e){
                if(parseInt(this.boxHeight)<e.path[0].offsetHeight){
                    this.boxHeight=e.path[0].offsetHeight+'px';
                }
            }
        }
    }
</script>
<style lang="scss">
    .ec-slide-img-box {
        width: 100%;
        height: 100%;
        position: relative;
        touch-action: none;
    }
    .ec-slide-img{
        width: 100%;
        height: 100%;
        img{
            max-width: 100%;
            max-height: 100%;
        }
    }
    .ec-slide-img-type{
        position: relative;
        overflow: hidden;
        width: 100%;
        height: 100%;
        &.ec-slide-img-top{
        }
        &.ec-slide-img-left{
            li {
                display: inline-block;
                font-size: 0;
            }
        }
        &.ec-slide-img-transparent {
            li {
                opacity: 0;
                transition: opacity 1s;
                width: 0;
                &.cur {
                    width: auto;
                }
                &.show{
                    opacity: 1;
                }
            }
        }
        ul {
            font-size: 0;
            &.tran {
                transition: all .4s;
            }
            li{
                text-align: center;
            }

            img {
                vertical-align: middle;
                max-width: 100%;
                max-height: 100%;
            }
        }
    }
    .ec-slide-arrow {
        div {
            position: absolute;
            z-index: 2;
            margin: auto;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            opacity: .5;
            &:hover{
                opacity: 1;
            }
            &.arrow-left {
                left: 10px;
                right: auto;
            }
            &.arrow-right {
                right: 10px;
                left: auto;
                transform: rotate(180deg);
            }
            &.arrow-top {
                top: 10px;
                bottom: auto;
            }
            &.arrow-bottom {
                bottom: 10px;
                top: auto;
                transform: rotate(180deg);
            }
        }
    }
    .ec-slide-option {
        position: absolute;
        font-size: 0;
        bottom: 10px;
        text-align: center;
        width: 100%;
        z-index: 5;
        &.isFirst {
            span:first-child {
                display: none;
            }
        }
        &.isLast {
            span:last-child {
                display: none;
            }
        }
        span {
            border-radius: 100%;
            margin: 0 5px;
            background: #fff;
            display: inline-block;
            width: 10px;
            height: 10px;
            &.active {
                background: #09f;
            }
        }
        &.ec-slide-option-top{
            display: table;
            width: 10px;
            height: 100%;
            top: 0;
            right: 10px;
            margin: auto;
            bottom: 0;
            span{
                margin:5px 0;
            }
            div{display: table-cell;vertical-align: middle;}
        }
    }
</style>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <title>Title</title>
    <style>
        body{

        }
        .transparent-left{
            margin-bottom: 20px;
            width: 500px;
        }
        .transparent-top{
            max-width: 1000px;
            margin-bottom: 20px;

        }
        .slider-left{
            width: 80%;
            margin: 20px auto;
            max-width: 1000px;
        }
        .slider-top{
            width: 1000px;
            height: 500px;
        }
        .slider-one{
            margin: 20px auto;
            width: 500px;
        }
    </style>
</head>
<body>
<div id="app6">
    <!--http://i2.kiimg.com/1949/098c291e8db16ab5.jpg          http://i1.buimg.com/1949/4d860a3067fab23b.jpg-->
    <div class="transparent-top">
        <ec-slide :list='list' :autoplay="true" :type="'transparent'" :option="true" :time="4000" :sildetype="'ease'" :arrowurl="'http://i2.kiimg.com/1949/098c291e8db16ab5.jpg'" :arrowsize="'40,20'" :direction="'top'"></ec-slide>
    </div>
    <div class="transparent-left">
        <ec-slide :list='list' :autoplay="true" :type="'transparent'" :option="true" :time="4000" :sildetype="'ease'" :arrowurl="'http://i1.buimg.com/1949/4d860a3067fab23b.jpg'" :arrowsize="'20,40'" :direction="'left'"></ec-slide>
    </div>
    <div class="slider-left">
        <ec-slide :list='list' :autoplay="true" :type="'slide'" :option="true" :time="4000" :sildetype="'ease'" :arrowurl="'http://i1.buimg.com/1949/4d860a3067fab23b.jpg'" :arrowsize="'20,40'" :direction="'left'"></ec-slide>
    </div>
    <div class="slider-top">
        <ec-slide :list='list' :autoplay="true" :type="'slide'" :option="true" :time="4000" :sildetype="'ease'" :arrowurl="'http://i2.kiimg.com/1949/098c291e8db16ab5.jpg'" :arrowsize="'40,20'" :direction="'top'"></ec-slide>
    </div>
    <div class="slider-one">
        <ec-slide :list='list2'></ec-slide>
    </div>
</div>
</body>
</html>

4.小結

好了,今天的開發就到此爲止了。起初這個項目我是打算當練手用的,可是後來在項目上使用了,雖然這個寫得比較簡單,可是效果還不錯。如今狀況還不是很好,之後有須要也會維護。目前來講,也是建議你們能夠玩下這個項目,雖然文章有點長,可是直接看下,邊動手寫代碼,邊看文章,會發現。一會兒就看完了!這個應該是不錯的練手項目,能夠熟悉使用vue開發組件!最後,若是你們以爲有哪裏寫錯了,寫得很差,歡迎指點!

-------------------------華麗的分割線--------------------
想了解更多,關注關注個人微信公衆號:守候書閣

clipboard.png

相關文章
相關標籤/搜索