最近使用swiper这个库,突然想做个无限轮播滚动的通知栏。效果如下图:
思路
这东西其实是一个比较特殊的轮播图效果,所以主要参照轮播图的开发流程来做就差不多了。
- 需要一个显示的内容的盒子包裹内容,高度按照内容的整体高度(cilentHeight )来决定,我比较懒,直接写死(差不多放下4个内容 ),四个内容为一版显示,后续都是根据4个为一组进行轮播显示
- 因为要进行滚动显示,只需要给内容外层容器添加translateY配合transition来达到滚动和缩放的动画效果。
- 后续需要计算translateY的每一次滚动的距离,所以内容之间最好不要有间隔,比如margin之类的间隔,因为这样子更加简单,后面计算的时候直接滚动对应的高度就好了。
- 要实现无限无缝滚动,就是要参照轮播图的无缝滚动了,原来其实就是复制第一份视图容器所能容下的内容数据,放到数据最后方,当滚动到最后的复制数据时,停止对应的动画,然后直接初始化数据我这里做得不够好,希望大伙帮我优化一下,哈哈哈。
实现
结构
HTML
<!-- container就是视图容器,这里写死一个高度 -->
<div class="container">
<!-- 内容外层容器 -->
<ul :ref="(el) => ulRef = el" class="user-list flex flex-col flex-aic" :style="{
transform: `translateY(${translateY}px)`
}">
<!-- 内容 -->
<li class="flex flex-aic flex-jcc" :ref="(el) => itemRef = el" v-for="(item,index) in list">
<div :id="`User${index}`" class="flex flex-aic gap-10 w-100p h-100p" :style="userStyle(index)">
<span>{{ item.name }}</span>
<span>has placed an enquiry on BetterYou products of <span style="color: #1A237C">{{ item.money }}</span>
pieces</span>
</div>
</li>
</ul>
</div>
样式:注释记得删掉
css
// 写死高度
.container {
margin: 100px auto;
height: 380px; // 这里是可以通过计算获得的,比较懒
overflow: hidden;
}
li::marker {
content: ""
}
.user-list li {
font-size: 18px;
font-family: Microsoft YaHei-Bold, Microsoft YaHei;
font-weight: 700;
color: #1A237C;
padding-bottom: 30px; // 这里是为了给内容之间看起来存在间隔
}
li div {
padding: 18px 40px;
width: fit-content;
background: rgba(182, 182, 182, 0.1);
box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.05);
border-radius: 10px 10px 10px 10px;
text-align: center;
}
li span:last-child {
color: #000000;
}
逻辑
js
import { defineComponent, ref,defineProps,computed,onMounted,onUnmounted } from 'vue';
// 原来使用TS写的,但是掘金居然不支持VUE3的TS版本,只能随便搬过来了
export default defineComponent({
setup() {
const data = ref([
{
name: "遥遥领先",
money: 99999
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
},
{
name: "大米",
money: 7777
}
])
const ulRef = ref(null) // 内容外层容器
const itemRef = ref(null) // 内容容器
const timer = ref(null)
const translateY = ref(0) // 内容外层容器滚动Y轴距离
const activeIndex = ref(0) // 当前最大的活动内容(就是视图容器中显示的第一个内容)
const delayed = ref(1) // 动画持续时间
const list = computed(
() => data.value?.length
? [
...data.value,
...data.value.filter((item, index) => index < 4) // 将最前面的四个内容复制一份放到最后
]
: []
)
const disY = computed(() => itemRef.value?.clientHeight ?? 90)
const userStyle = computed(() => (index) => {
let style = {}
switch (index) {
case activeIndex.value:
style['transform'] = `scale(1)`
style['transition'] = `all ${delayed.value}s`
break
case activeIndex.value + 1:
style['transform'] = `scale(.9)`
style['transition'] = `all ${delayed.value}s`
break
case activeIndex.value + 2:
style['transform'] = `scale(.8)`
style['transition'] = `all ${delayed.value}s`
break
case activeIndex.value + 3:
style['transform'] = `scale(.7)`
style['transition'] = `all ${delayed.value}s`
break
case activeIndex.value + 4:
// 视图之外第五个内容的样式,这样子动画看起来就不会很突兀
style['transform'] = `scale(.6)`
style['transition'] = `all ${delayed.value}s`
break
default:
// 视图容器之外(除了第五个内容)内容样式
style['transform'] = `scale(1)`
style['transition'] = 'none'
break
}
if (activeIndex.value === 0) style['transition'] = 'none' // 循环了一轮后,显示了复制前面的四个内容之后
return style
})
onMounted(() => {
start()
})
onUnmounted(() => {
clearInterval(timer.value)
})
function start() {
timer.value = setInterval(() => {
if (activeIndex.value === list.value.length - 4) {
// 循环了一轮之后初始化
ulRef.value.style.transition = 'none'
translateY.value = 0
activeIndex.value = 0
} else {
// 循环轮播
ulRef.value.style.transition = `all ${delayed.value}s`
translateY.value -= disY.value
activeIndex.value += 1
}
}, delayed.value * 1000)
}
return {
list,
translateY,
ulRef,
itemRef,
timer,
translateY,
activeIndex,
delayed,
userStyle
};
},
});