手寫一個移動端帶慣性的輪播圖vue組件


利用vue的插槽(solt)的方法實現

  1. 帶有慣性
  2. 可以適應手機端屏幕
  3. 可以靈活的修改樣式
  4. 可以手動的拖拽
  5. 拉力
調用方式:
  1. 引入組件
    export default defineComponent({
        components: {
            slide
        },

     

  2. 在template模板插入代碼
    <slide
                ref="slide"
                :count="swiperData.length"
                :springrange=".2"
            >
                <slot slot="swiper-item" v-for="(item, index) in swiperData">
                    <div class="emoji-item">
                    </div>
                </slot>
            </slide>

     

  3. 在style編swiper-item的樣式。

 


組件代碼:
<template>
    <div class="swiper-container">
        <div 
            class="swiper-touch" 
            ref="touch" 
            @touchstart="touchStart" 
            @touchmove="touchMove" 
            @touchend="touchEnd">
            <div
                class="swiper-wrapper"
                :style="{
                    transform: 'translate3d(' + slideEffect + 'px, 0px, 0px)',
                    transitionDuration: tdurationTime + 'ms',
                    width: count + '00%'
                }"
            >
                <slot name="swiper-item"></slot>
            </div>
        </div>
        <ul class="swiper-dots">
                <li 
                    v-for="num in count" 
                    :key="num" 
                    :class="currantIndex === num?'swiper-dots-active':''"
                    @click="handleChangeCurrantIndex(num)">
                </li>
        </ul>
    </div>
</template>


<script lang="ts">
import {
    VueCompositionApiLib as VC,
    toRefs,
    defineComponent,
    reactive,
    onMounted
} from 'common';

interface Props {
    count: number;
    springrange: number;
    tduration: number;
    isShowIndexBtn: boolean;
}
export default defineComponent({
    props: {
        count: {
            type: Number,
            default: 0
        },
        isShowIndexBtn: {
            type: Boolean,
            default: true
        },
        springrange: {
            type: Number,
            default: 0.2
        },
        tduration: {
            type: Number,
            default: 300
        }
    },
    setup(props: Props, context: VC.SetupContext) {
        const state = reactive({
            startX: 0, // 開始觸摸的位置
            moveX: 0, // 滑動時的位置
            endX: 0, // 結束觸摸的位置
            disX: 0, // 移動距離
            springWidth: 0, // 回彈的范圍屏幕的百分比0.2即左右五分之一
            slideEffect: 0, // 當前位移像素
            btnWidth: 0, // 頁面寬度
            currantIndex: 1, // 當前slide
            tdurationTime: 300, // 過渡時間
            lastEndSpead: 0, // 上次的位置
            status: 'a' // c不可以切換上一張,a可以上下切換,b不能切換下一張
        });

        onMounted(() => {
            state.btnWidth = context.refs.touch.offsetWidth;
            state.springWidth = props.springrange * state.btnWidth;
            state.tdurationTime = props.tduration;
        });

        const touchStart = (e: any) => {
            e = e || event;
            if (e.touches.length === 1) {
                state.startX = e.touches[0].clientX; // 記錄開始位置
            }
        };
        const touchMove = (e: any) => {
            e = e || event;
            state.tdurationTime = 0;
            state.moveX = e.touches[0].clientX;
            state.disX = state.moveX - state.startX;
            // 在邊界的時候做一些拉力的判斷            
            if(state.disX > 0 && state.currantIndex === 1){
                state.slideEffect = state.disX / 3 + state.lastEndSpead;
            }
            else if(state.disX < 0 && state.currantIndex === props.count)
                state.slideEffect = state.disX / 3 + state.lastEndSpead;
            else {
                state.slideEffect = state.disX + state.lastEndSpead;
            }
            // disX不考慮回彈時小於0下一張大於0上一張
        };

        const next = () => {
            state.slideEffect = -state.btnWidth + state.lastEndSpead;
            state.currantIndex += 1;
        };
        const last = () => {
            state.slideEffect = state.btnWidth + state.lastEndSpead;
            state.currantIndex -= 1;
        };
        const forbidChange = () => {
            state.slideEffect = state.lastEndSpead;
        };
        const actions: any = {
            'a': next,
            'b': last,
            'c': forbidChange
        };

        const touchEnd = (e: any) => {
            e = e || event;
            state.tdurationTime = props.tduration;
            state.endX = e.changedTouches[0].clientX;
            if (Math.abs(state.disX) < state.springWidth && state.currantIndex) {
                state.status = 'c';
            }
            else if (state.disX < 0 && state.currantIndex === props.count) {
                state.status = 'c';
            }
            else if (state.disX > 0 && state.currantIndex === 1) {
                state.status = 'c';
            }
            else if (state.disX > 0) {
                state.status = 'b';
            }
            else if (state.disX < 0) {
                state.status = 'a';
            }
            state.disX = 0;
            actions[state.status]();
            state.lastEndSpead = state.slideEffect;
        };

        const handleChangeCurrantIndex = (num: number) => {
            state.currantIndex = num;
            state.slideEffect = -state.btnWidth * (num - 1);
            state.lastEndSpead = state.slideEffect;
        };
        return {
            ...toRefs(state),
            touchStart,
            touchMove,
            touchEnd,
            handleChangeCurrantIndex
        };
    }
});
</script>



<style lang="less" scoped>
.swiper-container {
    overflow: hidden;
    z-index: 1;
    width: 100%;
    background: #F5F5F5;
    .swiper-dots {
        display: block;
        position: absolute;
        right: 0;
        width: 100%;
        bottom: .25rem;
        height: .06rem;
        list-style: none;
        text-align: center;
        & > li {
            width: .07rem;
            height: .07rem;
            margin: 0 .04rem;
            background: #ccc;
            display: inline-block;
            vertical-align: top;
            border-radius: 50%;
            opacity: .45;
        }
        .swiper-dots-active {
            opacity: 1;
        }
    }
}

.swiper-touch {
    width: 100%;
    position: relative;
    .swiper-wrapper {
        display: flex;
        position: relative;
        transition-property: transform, height, -webkit-transform, -moz-transform, -o-transform;
    }
}
</style>

 

總結:

  1. 相比於其他組件庫的組件可能用起來不夠簡單
  2. 但是也有自己的優點即靈活度比較高,可以根據自己的想法擴展成自己的組件
  3. 可以隨意的填寫自己的樣式,做成自己想要的樣子

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM