1. 需求+效果 展示
最近做开发时有这样一个需求,滚动播放列表,数据有限,当这些数据滚动播放结束后再重头开始,做完之后的效果是这样的
2. 实现代码(带标注)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>翻页功能</title>
</head>
<body>
<script>
let intervalTimer; // 不断执行的定时器
let intervalDelay = 1000; // 定时器执行时间
let scrollSpeed = 10; // 移动速度
let scrollHeight = 22; // 移动距离,一页/一行的高度
const rollContent = [
{
name: '第一条',
data: 1,
},
{
name: '第二条',
data: 2,
},
{
name: '第三条',
data: 3,
},
{
name: '第四条',
data: 4,
},
{
name: '第五条',
data: 5,
},
{
name: '第六条',
data: 6,
},
{
name: '第七条',
data: 7,
},
{
name: '第八条',
data: 8,
},
{
name: '第九条',
data: 9,
},
{
name: '第十条',
data: 10,
}
]
// 开始令动画运行
function initAnimation() {
// 将所有内容渲染在页面中
// 创意一个div,将内容ul放置其中,div设置滚动
const div = document.createElement('div');
// 创建一个ul,用于放置li
const ulList = document.createElement('ul');
div.appendChild(ulList)
// 获取外部容器body
const body = document.getElementsByTagName('body')[0]
// 将ul动画插入页面中
body.appendChild(div)
// 插入li,li循环展示rollContent中的内容
for (let i = 0; i < rollContent.length; i++) {
const listItem = document.createElement('li');
listItem.style = "padding:0 4px;"
listItem.innerText = rollContent[i].name + '---' + rollContent[i].data
ulList.appendChild(listItem)
}
// 两次循环插入循环内容的两倍,为了在切换时效果更好,理论上加上一页的数据即可,不用插入两倍,但若加上一页的数据,下面处理回到顶部的距离需要重新计算
for (let i = 0; i < rollContent.length; i++) {
const listItem = document.createElement('li');
listItem.innerText = rollContent[i].name + '---' + rollContent[i].data
ulList.appendChild(listItem)
}
// 开启定时器,令内容动起来
setTimeout(() => {
moveContent();
}, intervalDelay)
}
function moveContent() {
// 移动
const moveDiv = document.getElementsByTagName('div')[0]
const ul = document.getElementsByTagName('ul')[0]
ul.style="padding-left:0;margin:0;"
// 设置样式,令其overflow:scroll;
moveDiv.style = "height:22px;overflow:scroll;background:lightblue;border-radius:8px";
// 设置移动距离
moveDiv.scrollTop ++;
// 移动时并判断是不是要返回顶部
intervalTimer = setInterval(() => {
moveTop();
}, scrollSpeed);
}
// 判断是否返回到顶部
function moveTop() {
const moveDiv = document.getElementsByTagName('div')[0]
const mul = document.getElementsByTagName('ul')[0]
// 若当前移动距离,与ul高度求余时所得结果为0,表示翻了一页,此时清空定时器,走第二个
if (moveDiv.scrollTop % scrollHeight === 0) {
// 清除判断移动距离定时器
clearInterval(intervalTimer)
setTimeout(() => {
moveContent()
}, intervalDelay);
}else{
// 一点点移动,才有平滑滚动的效果
moveDiv.scrollTop++;
if(moveDiv.scrollTop >= mul.offsetHeight/2){
moveDiv.scrollTop = 0;
}
}
}
window.onload = function () {
initAnimation();
}
</script>
</body>
</html>
3. 原理解析
- 实现翻页效果,看上去有两个滚动,一个是外部容器的scrollTop,一个是每一条数据移动,但实际上是通过两个定时器根据不同的条件判断得来
- 我们需要一个最外部容器div,设置内容展示的宽高、样式等
- 我们需要一个循环数据的容器ul,用于包裹内部li内容,最后在根据他的offsetHeight属性决定何时返回顶部
- 开启动画,因为是翻页效果,所以当一页移动完成后,我们需要停顿一段时间,再进行下一次的翻页
- 翻页过程要求平滑过渡,所以我们可以设置当前div属性的scrollTop++,令其平滑移动
- 在每次平滑移动的过程中,我们需要增加一个定时器进行判断,当前滑动的距离求余每一页的固定距离是否为0,若为0表示一页内容结束,清除定时器,重新进行scrollTop++;若一页内容未结束,我们继续scrollTop++
- 判断是否为一页,是否继续scrollTop++时我们要记得进行判断,因为我们是两倍的当前内容,所以当scrollTop的值 >= 当前ul的offsetHeight的一半时,就可以令scrollTop回到顶部继续执行。
4.自适应场景
我们可以通过改变延迟时间、移动速度、移动高度来实现自己所需的效果
5. 在vue中的应用
<div class="applyAnimation-scroll">
<ul class="applyAnimation-list">
<ApplyAnimationItem // 自定义组件,将所需内容传入
v-for="(item,index) in latestCustomers"
:key="index" :applyItem="item"/>
</ul>
</div>
ref: latestCustomers = []; // 定义循环播放数组 vue3 script setup写法
let interval = null; // 定时器
// 获取数据
async function getLatestCustomers() {
const {data} = await tGet(`aaaa/bbbb`, {
count: 20
})
latestCustomers = data;
}
let itemHeight = 34;
let delay = 1000;
let speed = 10;
let time;
async function initScrollAnimation() {
let area = document.getElementsByClassName('applyAnimation-scroll')[0];
let list = document.getElementsByClassName('applyAnimation-list')[0];
let finalHeight = list.offsetHeight;
area.innerHTML += area.innerHTML;
area.scrollTop = 0;
setTimeout(() => {
starMove(area, finalHeight)
}, delay);
}
async function starMove(area, finalHeight) {
area.scrollTop++;
time = setInterval(() => {
scrollUp(area, finalHeight)
}, speed);
}
async function scrollUp(area, finalHeight) {
if (area.scrollTop % itemHeight === 0) {
clearInterval(time);
setTimeout(() => {
starMove(area, finalHeight)
}, delay);
} else {
area.scrollTop++;
if (area.scrollTop >= finalHeight) {
area.scrollTop = 0;
}
}
}
onMounted(async () => {
await getLatestCustomers();
await initScrollAnimation();
})
总结用法,希望可以帮助到你,
我是Ably,你无须超越谁,只要超越昨天的自己就好~