手写一个移动端带惯性的轮播图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',
                     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;
     100%;
    background: #F5F5F5;
    .swiper-dots {
        display: block;
        position: absolute;
        right: 0;
         100%;
        bottom: .25rem;
        height: .06rem;
        list-style: none;
        text-align: center;
        & > li {
             .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 {
     100%;
    position: relative;
    .swiper-wrapper {
        display: flex;
        position: relative;
        transition-property: transform, height, -webkit-transform, -moz-transform, -o-transform;
    }
}
</style>

总结:

  1. 相比于其他组件库的组件可能用起来不够简单
  2. 但是也有自己的优点即灵活度比较高,可以根据自己的想法扩展成自己的组件
  3. 可以随意的填写自己的样式,做成自己想要的样子
原文地址:https://www.cnblogs.com/tipsydr/p/13784787.html