VUE实现大小缩放轮播图

效果图

<template>
    <view class="swiper-container" ref="root" @touchstart="onTouchStart" @touchend="onTouchEnd">
        <view @click="evtChangeIndex(index)" class="side" v-for="(item, index) in state.source" :key="index"
            :style="item.sty" :class="item.className">
            <img :src="item?.img" alt="">
        </view>
    </view>
</template>

<script>

import { ref, computed, reactive, watch, onMounted } from 'vue';

export default {
    props: {

        /**
         * 轮播数据来源
         */
        source: {
            type: Array,
            default: () => [
                { img: require('@/assets/imgs/test/1.png') },
                { img: require('@/assets/imgs/test/1.png') },
                { img: require('@/assets/imgs/test/1.png') },
                { img: require('@/assets/imgs/test/1.png') },
                { img: require('@/assets/imgs/test/1.png') },
            ],
        },
        /**
         * 初始激活的索引
         */
        initial: {
            type: Boolean,
            default: 0
        },
        /**
         * 自动播放时间
         */
        interval: {
            type: Number,
            default: 3500
        },

    },
    emits: ['update:active'],
    setup(props, { emit }) {

        /**
         * 轮播数据源
         */
        let source = props.source;

        /**
         * 轮播数据源长度差
         */
        let diff = source.length - 5;

        // 不足5个数据,补全
        if (diff < 0) {
            //缺几个补几个
            diff = Math.abs(diff);
            source.slice(0, diff).forEach(item => {
                source.push(item);
            })
        }

        /**
         * 处理每一项的样式
         * @param {*} initial 当前项 
         * @param {*} source  数据源
         */
        const computed = (initial, source) => {
            // 当前项的索引
            let len = source.length;

            //先处理中间值
            initial = initial < 0 ? 0 : (initial >= len ? len - 1 : initial);

            //5个项的temp
            let temp1 = initial - 2;
            let temp2 = initial - 1;
            let temp3 = initial;
            let temp4 = initial + 1;
            let temp5 = initial + 2;

            //处理边界值
            temp1 = temp1 < 0 ? len + temp1 : temp1;
            temp2 = temp2 < 0 ? len + temp2 : temp2;

            //超界行为
            temp4 = temp4 >= len ? temp4 - len : temp4;
            temp5 = temp5 >= len ? temp5 - len : temp5;

            //计算每一项样式
            return source.map((item, index) => {
                let transform = 'translate(-50%, -50%) scale(0.55)',
                    zIndex = 0,
                    className = 'side';

                switch (index) {
                    //中间的
                    case temp3:
                        zIndex = 3;
                        className = 'side active';
                        transform = 'translate(-50%, -50%) scale(1)';
                        break;
                    case temp1:
                        zIndex = 1;
                        className = 'side active';
                        transform = 'translate(-160%, -50%) scale(0.8)';
                        break;
                    case temp2:
                        zIndex = 2;
                        className = 'side active';
                        transform = 'translate(-110%, -50%) scale(0.9)';
                        break;
                    case temp4:
                        zIndex = 2;
                        className = 'side active';
                        transform = 'translate(10%, -50%) scale(0.9)';
                        break;
                    case temp5:
                        zIndex = 1;
                        className = 'side active';
                        transform = 'translate(60%, -50%) scale(0.8)';
                        break;
                }

                item.sty = {
                    zIndex,
                    transform
                }
                item.className = className;
                return item;
            })
        }

        source = computed(props.initial, source);

        /**
         * 构建响应式
         */
        const state = reactive({
            initial: props.initial,
            source
        })


        let autoTimer = null;

        /**
         * 监听激活数据变化
         */
        watch(() => state.initial, (initial, preInitial) => {
            state.source = computed(initial, state.source);
        })

        /**
         * 自动轮播
         */
        const autoPlay = () => {
            autoTimer = setInterval(() => {
                state.initial++;
                //边界处理
                if (state.initial >= state.source.length) {
                    state.initial = 0;
                }
            }, props.interval);
        }

        /**
     * 手势处理逻辑
     */
        let startX = 0;
        let endX = 0;
        const onTouchStart = (e) => {
            startX = e.touches[0].clientX;
            //手势关闭自动滚动
            stopAutoPlay();
        };
        const onTouchEnd = (e) => {
            endX = e.changedTouches[0].clientX;
            if (startX > endX + 50) {
                // 左滑
                change('right');
            } else if (startX < endX - 50) {
                // 右滑
                change('left');
            }
            //手势识别后重启
            autoPlay();
        };

        /**
         * 停止自动播放
         */
        const stopAutoPlay = () => {
            clearInterval(autoTimer);
        }

        let root = ref(null);

        //开启自动轮播
        onMounted(() => {
            //开启自动轮播
            autoPlay();
            let box = root.value;

            // box.onmouseenter = function () {
            //     clearInterval(autoPlay);
            // }
            // box.onmouseleave = function () {
            //     autoPlay();
            // }
        })

        /**
         * 切换
         * @param dir left:上一张,right:下一张
         */
        const change = (dir) => {
            if (dir == 'left') {
                state.initial--;
                if (state.initial < 0) {
                    state.initial = state.source.length - 1;
                }
            }
            if (dir == 'right') {
                state.initial++;
                //超出边界定向到0
                if (state.initial >= state.source.length) {
                    state.initial = 0;
                }
            }
        }


        /**
         * 轮播切换 具体值
         */
        const evtChangeIndex = (index) => {
            state.initial = index;
            //重启定时
            stopAutoPlay();
            autoPlay();
        }

        return {
            state,
            root,
            change,
            evtChangeIndex,
            onTouchStart,
            onTouchEnd
        }

    },
};
</script>

<style lang="scss">
.swiper-container {
    width: 360px;
    display: flex;
    height: 200px;
    justify-content: center;
    /* 水平方向居中 */
    align-items: center;
    /* 垂直方向居中 */
    position: relative;
    background: transparent;
    overflow: hidden;

    /* 隐藏超出部分 */
    .side {
        position: absolute;
        width: 35%;
        height: 200px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) scale(1);
        transition: 0.5s linear;
        border-radius: 3px;
        overflow: hidden;

        img {
            height: 100%;
            width: 100%;
        }
    }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值