代码拉取完成,页面将自动刷新
<template>
<div class="VirtualSwiper-container"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave">
<div class="img-wrap"
:style="{
transform,
transition,
}"
@transitionend="onTransitionEnd">
<div class="img-item">
<div v-for="(item, index) in _data"
:key="index"
:style="{
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: `${index * 100 + (
index === 0
? left1 * 100
: left2 * 100
)}%`,
}">
<slot :data="item">
<img v-if="item.src"
:src="item.src"
alt=""
:style="{
display: 'block',
height: '100%',
width: '100%',
objectFit: 'cover',
}"
:title="item.desc ?? ''" />
<div v-else>没有`src`属性 请使用插槽自定义</div>
</slot>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts" generic="T extends {
src?: string
[K: string]: any
}">
/**
* 虚拟轮播图 超高性能 无缝轮播
* 就算有再多的图片 也只会加载两张
*/
defineOptions({
inheritAttrs: true,
name: 'VirtualSwiper'
})
const props = withDefaults(
defineProps<{
data: T[]
duration?: number
autoPlay?: boolean
needHoverPause?: boolean
transitionMs?: number
}>(),
{
autoPlay: true,
needHoverPause: true,
duration: 1000,
transitionMs: 500,
}
)
const curIndex = ref(0),
timer = ref<number>(),
/** 两张图轮流交换实现无缝虚拟轮播 */
imgIndexArr = ref([curIndex.value, curIndex.value + 1]),
/** 不停切换两张图 实现虚拟无缝轮播 */
_data = computed(() => [
props.data[imgIndexArr.value[0]],
props.data[(imgIndexArr.value[1]) % props.data.length]]
),
transformIndex = ref(0),
transition = ref(`transform ${props.transitionMs}ms`),
transform = computed(() => `translateX(-${transformIndex.value * 100}%)`)
/** ---------------------------------------------------------------------------
* 初始化入口
*/
init()
function init() {
play()
bindEvent()
}
/** ---------------------------------------------------------------------------
* 事件
*/
/** 需要修改位置的索引 当首次滚动后 把第一张图片继续放到最后 循环往复 */
let curChangeIndex = 0
const left1 = ref(0),
left2 = ref(0)
function onTransitionEnd() {
curIndex.value = (curIndex.value + 1) % props.data.length
changeIndexByNext()
}
function onMouseEnter() {
if (!props.needHoverPause) {
return
}
stop()
}
function onMouseLeave() {
if (!props.needHoverPause) {
return
}
play()
}
/** 离开浏览器时 不会执行`transitionend` 所以要停止 */
function bindEvent() {
window.addEventListener('visibilitychange', () => {
document.hidden
? stop()
: play()
})
}
/** ---------------------------------------------------------------------------
* 工具函数
*/
/** 下一张图的切换逻辑 */
function changeIndexByNext() {
if (curChangeIndex === 0) {
left1.value += 2
curChangeIndex = 1
const i = (imgIndexArr.value[0] + 2) % props.data.length
imgIndexArr.value[0] = i
}
else if (curChangeIndex === 1) {
curChangeIndex = 0
/** 以下两种方式都能实现无缝轮播 但是下面的 可以避免位移的值无限变大 */
// left2.value += 2
resetPos()
const i = (imgIndexArr.value[1] + 2) % props.data.length
imgIndexArr.value[1] = i
}
}
function resetPos() {
transition.value = 'none'
transformIndex.value = 0
left1.value = 0
left2.value = 0
}
function next() {
setTransition()
transformIndex.value++
}
function play() {
if (props.data.length < 2 || !props.autoPlay) return
stop()
timer.value = window.setInterval(next, props.duration)
}
function stop() {
timer.value && clearInterval(timer.value)
timer.value = undefined
}
function setTransition() {
transition.value = `transform ${props.transitionMs}ms`
}
</script>
<style lang="scss" scoped>
.VirtualSwiper-container {
height: 100%;
overflow: hidden;
}
.img-wrap {
height: 100%;
}
.img-item {
position: relative;
height: 100%;
}
</style>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。