手势滑动切换轮播图

537 阅读2分钟

开发中,实现滑动切换轮播图是一个常见的需求。本文将介绍如何使用Touch手势来实现这一功能,为应用添加更加灵活和直观的界面交互效果。

效果

output.gif

代码

<template>
    <view class="sliding-swiper">
        <view
            class="item"
            v-for="item in listNew"
            :key="item.key"
            :style="[itemStyle(item.sort)]"
            :class="[
                {
                    'prev-active': animateType == 'prev',
                    'next-active': animateType == 'next',
                },
            ]"
            @touchstart="touchstart($event)"
            @touchmove="touchmove($event)"
            @touchend="touchend($event)">
            <view class="image-wrap">
                <image
                    :src="item.image"
                    mode="aspectFill"
                    class="image"></image>
            </view>
            <view class="line-1 title"> {{ item.key }}-{{ item.title }} </view>
        </view>
    </view>
</template>

<script>
export default {
    props: {
        list: {
            type: Array,
            default: [],
        },
    },
    data() {
        return {
            listNew: [],
            animateType: "",
            startX: 0,
            moveX: 0,
            curIndex: 0,
        };
    },
    computed: {
        itemStyle() {
            return (index) => {
                let translateX = `translateX(${index > 2 ? 0 : index * 20}%)`;
                let scale = `scale(${1 - index / 10})`;
                return {
                    transform: `${translateX} ${scale}`,
                    zIndex: `${this.listNew.length - index}`,
                };
            };
        },
    },
    mounted() {
        this.listNew = this.list.map((item, index) => {
            item.sort = index;
            item.key = `index_${index}`;
            return item;
        });
    },
    methods: {
        change(type = "next") {
            this.animateType = type;
            let last = this.listNew.length - 1;
            if (type == "next") {
                this.curIndex = this.curIndex == last ? 0 : this.curIndex + 1;
                this.listNew = this.listNew.map((item, index) => {
                    item.sort = index == this.curIndex ? 0 : item.sort == 0 ? last : item.sort - 1;
                    return item;
                });
            }
            if (type == "prev") {
                this.curIndex = this.curIndex == 0 ? last : this.curIndex - 1;
                this.listNew = this.listNew.map((item, index) => {
                    item.sort = index == this.curIndex ? 0 : item.sort == last ? 0 : item.sort + 1;
                    return item;
                });
            }
            this.$nextTick(() => {
                this.animateType = "";
            });
        },
        touchstart(e) {
            this.startX = e.touches[0].clientX;
        },
        touchmove(e) {
            this.moveX = e.touches[0].clientX;
        },
        touchend(e) {
            let distance = this.moveX - this.startX;
            this.change(distance < 100 ? "next" : "prev");
        },
    },
};
</script>

<style scoped lang="scss">
.sliding-swiper {
    width: 100%;
    display: grid;
    .item {
        // 使item重叠
        grid-area: 1 / 1;
        position: relative;
        width: 80%;
        transition: 0.3s;
        border-radius: 10rpx;
        overflow: hidden;
        box-shadow: 0 4rpx 10rpx 0 rgba(0, 0, 0, 0.1);
        &.prev-active {
            animation: prev 0.5s ease-out;
        }
        @keyframes prev {
            20% {
                transform: translateX(-10%);
            }
            60% {
                transform: translateX(-60%);
            }
            100% {
                transform: translateX(-100%) scale(0.5);
            }
        }
        &.next-active {
            animation: next 0.5s ease-out;
        }
        @keyframes next {
            20% {
                transform: translateX(-80%);
            }
            60% {
                transform: translateX(-20%) scale(1);
            }
            100% {
                transform: translateX(0) scale(1);
            }
        }
        .image-wrap {
            width: 100%;
            height: 240rpx;
            box-sizing: border-box;
            .image {
                width: 100%;
                height: 100%;
            }
        }
        .title {
            padding: 20rpx 10rpx;
            width: 100%;
            background-color: #fff;
            box-sizing: border-box;
        }
    }
}
</style>

  • startX 和 moveX:记录触摸开始和移动中的X坐标。
  • touchStarttouchMove 和 touchEnd:处理触摸事件,计算滑动距离,并根据滑动方向切换轮播图。

使用

<template>
    <view class="content">
        <navbar
            backIconColor="#fff"
            titleColor="#fff"
            title="首页"
            :isBack="true"
            :background="{
                background: 'linear-gradient(144deg,#af40ff,#4f46e5)',
            }"></navbar>
            <view style="display:flex;justify-content: space-between;box-sizing:border-box;background-color: #fff; margin: 20rpx;padding: 20rpx;border-radius: 10rpx;overflow: hidden;">
                <view style="margin-right: 20rpx">
                    <image style="width: 160rpx; height: 160rpx"  src="https://img0.baidu.com/it/u=360986675,4290492095&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500" mode="aspectFill" :lazy-load="true">
                    </image>
                </view>
                    <view style="flex: 1">
                        <view style="margin-bottom: 20rpx;font-size: 30rpx;font-weight: bold;">
                            塞上风沙随马蹄,草原如画逐风吹。天高云淡思无限,万里胡天魂梦追。
                        </view>
                        <view style="width: 100%;padding-right: 20rpx;box-sizing: border-box;">
                            <slidingSwiper :list="slidingSwiperList"></slidingSwiper>
                        </view>
                </view>
            </view>
    </view>
</template>

<script>
import navbar from "@/components/navbar/navbar.vue";
import slidingSwiper from "@/components/slidingSwiper/slidingSwiper.vue";
export default {
    component: {
        navbar,
        slidingSwiper
    },
    data() {
        return {
            slidingSwiperList: [
                {
                    image: "https://cdn.uviewui.com/uview/swiper/swiper2.png",
                    title: "昨夜星辰昨夜风,画楼西畔桂堂东",
                },
                {
                    image: "https://cdn.uviewui.com/uview/swiper/swiper1.png",
                    title: "身无彩凤双飞翼,心有灵犀一点通",
                },
                {
                    image: "https://cdn.uviewui.com/uview/swiper/swiper3.png",
                    title: "谁念西风独自凉,萧萧黄叶闭疏窗,沉思往事立残阳",
                },
                {
                    image: "https://img1.baidu.com/it/u=3915920165,3171018890&fm=253&fmt=auto&app=120&f=JPEG",
                    title: "星斗闪烁银河挂,人间天地一片霞",
                },
                {
                    image: "https://img1.baidu.com/it/u=922847932,2985620342&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800",
                    title: "万里草原秋色多,风吹草低见牛羊",
                },
                {
                    image: "https://img2.huashi6.com/images/resource/2021/07/02/9094588h7p0.jpg?imageMogr2/quality/100/interlace/1/thumbnail/2000x%3E",
                    title: "海内存知己,天涯若比邻",
                },
            ],
        };
    },
    onLoad() {},
    methods: {},
};
</script>

uniapp自定义导航栏