超级悠悠
高性能程序定制、UI定制
542446

访问

0

评论

19

动态

2026

运行

来过
设计群: 70888820
位置: luzhou·sichuan

- 今日签到 -

首页破碎代码极致精简的Carousel走马灯

极致精简的Carousel走马灯

评论 0/访问 1375/分类: 破碎代码/发布时间:

carousel.vue

<template>
    <div class="uui-carousel" :style="getStyle" @mouseenter="handleStop" @mouseleave="handleContinue">
        <ul class="carousel-body">
            <li v-for="(item, i) in props.data" :key="i" class="carousel-item" :class="{ fade: i === index }">
                <img :src="item" :style="imgStyle">
            </li>
        </ul>
        <div class="carousel-btn prev" @click="handleToggleFN(-1)">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="28" height="28"><path fill="#ffffff" d="M10.26 3.2a.75.75 0 0 1 .04 1.06L6.773 8l3.527 3.74a.75.75 0 1 1-1.1 1.02l-4-4.25a.75.75 0 0 1 0-1.02l4-4.25a.75.75 0 0 1 1.06-.04z" /></svg>
        </div>
        <div class="carousel-btn next" @click="handleToggleFN(1)">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="28" height="28"><path fill="#ffffff" d="M5.74 3.2a.75.75 0 0 0-.04 1.06L9.227 8L5.7 11.74a.75.75 0 1 0 1.1 1.02l4-4.25a.75.75 0 0 0 0-1.02l-4-4.25a.75.75 0 0 0-1.06-.04z" /></svg>
        </div>
        <div class="carousel-indicator">
            <span v-for="i in props.data.length" :key="i" :class="{ active: index === i - 1 }" @click="index = i - 1" />
        </div>
    </div>
</template>
<script setup lang="ts">
import './less/app.less'
/**
 * 定义组件属性
 */
const props = withDefaults(
    defineProps<{
        //数据源
        data: string[],
        //切换速度
        duration?: number,
        //是否自动播放
        autoplay?: boolean,
        //容器高度
        height?: string,
        //容器宽度
        width?: string,
        //适应容器
        fit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
    }>(), {
        duration: 2,
        autoplay: true,
        height: '380px',
        width: '100%',
        fit: 'cover'
    }
)
const getStyle = ref('height:' + props.height)
const imgStyle = ref('object-fit:' + props.fit)
/**
 * 定义轮播图的默认索引值
 */
const index = ref(0)
/**
 * 定义定时器标识符
 */
const timer = ref<NodeJS.Timeout>()
/**
 * 定义轮播图方法
 */
const handleAutoplay = () => {
    //默认先清除当前定时器标识符以防止定时器重叠
    clearInterval(timer.value)
    //定义定时器
    timer.value = setInterval(() => {
        //改变轮播图的默认索引值
        index.value++
        //当轮播图的默认索引值超出轮播图数据的长度时恢复为默认值 0
        if (index.value >= props.data.length) index.value = 0
    }, props.duration * 1000)//根据父组件传的时间数据设置定时器的间隔时间
}
/**
 * 鼠标悬停至区域时-->轮播图停止|继续
 */
const handleStop = () => {
    //通过清除定时器来停止轮播
    if (timer.value) clearInterval(timer.value)
}
/**
 * 继续轮播
 */
const handleContinue = () => {
    //继续轮播时需判断是否为自动播放并且轮播图大于两张
    if (props.data.length > 1 && props.autoplay) handleAutoplay()
}
/**
 * 点击左右按钮跳转轮播图
 */
const handleToggleFN = (num: number) => {
    //修改index
    index.value += num
    //当轮播图的默认索引值超出轮播图数据的长度时恢复为默认值0
    if (index.value >= props.data.length) return (index.value = 0)
    //当轮播图的默认索引值小于0时-->跳至最后一个索引值
    if (index.value < 0) index.value = props.data.length - 1
}
/**
 * 侦听数据源数据变化-->是否启用轮播图
 */
watch(() => props.data, () => {
    //如果父组件所传数据为自动播放并且轮播图大于两张时轮播图开启
    if (props.data.length > 1 && props.autoplay) {
        //轮播开始前重置下标
        index.value = 0
        //调用轮播图方法
        handleAutoplay()
    }
}, { immediate: true })//页面创建时就进行侦听
/**
 * 在组件销毁时清除定时器避免性能损耗
 */
onUnmounted(() => {
    if (timer.value) clearInterval(timer.value)
})
</script>

app.less

.uui-carousel {
    position: relative;
    &:hover {
        .carousel-btn {
            opacity: 1;
        }
    }
    .carousel-body {
        width: 100%;
        height: 100%;
        .carousel-item {
            width: 100%;
            height: 100%;
            position: absolute;
            left: 0;
            top: 0;
            opacity: 0;
            transition: opacity 0.5s linear;
            &.fade {
                opacity: 1;
                z-index: 1;
            }
            img {
                width: 100%;
                height: 100%;
            }
        }
    }
    .carousel-btn {
        width: 38px;
        height: 38px;
        display: flex;
        cursor: pointer;
        align-items: center;
        justify-content: center;
        background: rgba(0, 0, 0, 0.2);
        color: #fff;
        border-radius: 50%;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        z-index: 2;
        text-align: center;
        opacity: 0;
        transition: all 0.3s;
        &:hover {
            opacity: 0.7;
        }
        &.prev {
            left: 20px;
        }
        &.next {
            right: 20px;
        }
    }
    .carousel-indicator {
        position: absolute;
        left: 0;
        bottom: 16px;
        z-index: 2;
        width: 100%;
        text-align: center;
        span {
            width: 12px;
            height: 12px;
            display: inline-block;
            background-color: rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            margin: 0 4px;
            cursor: pointer;
            transition: background-color .3s cubic-bezier(.4, 0, .2, 1);
            &.active {
                background: var(--uui-theme);
            }
        }
    }
}

使用方式

<template>
    <Carousel :data="data" />
</template>
<script setup lang="ts">
const data = [
    'https://picsum.photos/id/11/300/300',
    'https://picsum.photos/id/22/800/800',
    'https://picsum.photos/id/33/900/900',
    'https://picsum.photos/id/44/500/500',
    'https://picsum.photos/id/55/600/600'
]
</script>

收藏

点赞

打赏