循环滚动列表浅析

日常开发过程中,列表是比较常见的展示数据的一种形式,本文主要聚焦于列表的滚动以及循环滚动。

准备条件

由于存在动态数据,本文为了方便,基于 vue3 来实现的,当然原理是相通的,读者可以基于其他框架,或者直接对 dom 操作进行实现,在此不再赘述。

基本骨架

vue 复制代码
<template>
  <div class="list">
    <div class="list-item" v-for="(item) in items" :key="item.id">{{ item.name }}</div>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue';

const datas = ref(
  [...Array(100).keys()].map((i) => ({ id: i, name: `item ${i}` }))
);

const items = computed(() => datas.value)
</script>
<style scoped>
.list {
  height: 180px;
  width: 500px;
  border: 1px solid #eee;
  overflow: hidden;
}
.list-item {
  height: 30px;
  line-height: 30px;
  box-sizing: border-box;
  padding: 0 12px;
  border-bottom: 1px solid #eee;
}
.list-item:last-child {
  border-bottom-color: transparent;
}
</style>

在此我们定义了一个尺寸为 500 * 100的列表,每条数据占据 30 的高度,我们不想让它支持鼠标滚动,所以设置了overflow:hidden,大致效果图如下

实现

准备工作已经完成,是时候实现我们的目标了,怎么做呢?

动起来

如何动起来呢,其实大体有两种方式

  1. 通过 js 来操作滚动条,这种方式虽然能达到目的,但动画效果都得自己去实现,或者调用浏览器的平滑滚动,其实效果也是一言难尽
  2. 有没有比较好方式呢,当然有,那就是通过 css 动画实现了,通过让内层元素慢慢向上平移不就达到了滚动的效果么

在之前得改造下结构,因为我们需要一个容器来做平移,改造后的 html 结构如下

html 复制代码
<div class="list">
  <div class="list-wrapper" :style="{transform:`translateY(${ty})`}">
    <div class="list-item" v-for="(item) in items" :key="item.id">{{ item.name }}</div>
  </div>
</div>
js 复制代码
const ty = ref(0)

现在就需要改变这个 ty,从而让列表动起来,但这也需要通过 js 去定时改变它,但我们不想要这么做,所以我们继续改造,使用 cssanimation去实现,由于列表长度不固定,我们需要用到css变量。

首先定义动画,--anim-max-yy方向最大平移距离,--anim-duration动画持续时间

css 复制代码
@keyframes scroll-up {
  to {
    transform: translateY(var(--anim-max-y));
  }
}
.list-wrapper {
  animation: scroll-up var(--anim-duration) linear infinite;
}

我们的列表可视区域元素为 6 个,每行占 30px,排除掉可视区域的高度,就是我们最大的滚动距离,持续时间设置为元素个数/2对应的秒数

html 复制代码
<div class="list">
  <div
    class="list-wrapper"
    :style="{
      '--anim-max-y': `-${(items.length - 6) * 30}px`,
      '--anim-duration': `${items.length / 2}s`,
    }">
    <div
      class="list-item"
      v-for="item in items"
      :key="item.id">
      {{ item.name }}
    </div>
  </div>
</div>

至此我们已经大体实现了我们的需求,看起来效果还不错。为啥说是大体呢,因为我们既然说是循环滚动,那么衔接是不是应该平滑呢,仔细一看,当我们列表滚动到底部的时候,会看到一下子跳到头部,这就是需要我们改进的地方。

其实解决方案比较简单,就是往列表底部附加点可视区域内的数据,这样就能完美的解决问题了

js 复制代码
const visibleCount = 6
const items = computed(() => {
  if (datas.value.length <= visibleCount) {
    return datas.value;
  }
  return datas.value.concat(datas.value.slice(0, visibleCount))
});

至此,大功告成~~~

完整代码

vue 复制代码
<template>
<div class="list">
  <div
    class="list-wrapper"
    :style="{
      '--anim-max-y': `-${(items.length - visibleCount) * 30}px`,
      '--anim-duration': `${items.length / 2}s`,
    }">
    <div
      class="list-item"
      v-for="item in items"
      :key="item.id">
      {{ item.name }}
    </div>
  </div>
</div>
</template>
<script setup>
import { ref, computed } from "vue";

const datas = ref(
  [...Array(100).keys()].map((i) => ({ id: i, name: `item ${i}` }))
);

const visibleCount = 6
const items = computed(() => {
  if (datas.value.length <= visibleCount) {
    return datas.value;
  }
  return datas.value.concat(datas.value.slice(0, visibleCount))
});
</script>
<style scoped>
.list {
  height: 180px;
  width: 500px;
  border: 1px solid #eee;
  overflow: hidden;
}
.list-item {
  line-height: 30px;
  height: 30px;
  box-sizing: border-box;
  padding: 0 12px;
  border-bottom: 1px solid #eee;
}
.list-item:last-child {
  border-bottom-color: transparent;
}
@keyframes scroll-up {
  to {
    transform: translateY(var(--anim-max-y));
  }
}
.list-wrapper {
  animation: scroll-up var(--anim-duration) linear infinite;
}
</style>

后记

上面实现的滚动列表只是一种实现方式,当然大家可以进一步封装成组件,每一项的高度也可以通过变量进行设置,这一块就靠读者自由发挥。

相关推荐
梦帮科技3 小时前
GRAVIS v4.0:基于Web的极速套利架构设计与实时数据流实现
前端·人工智能·rust·自动化·区块链·智能合约·数字货币
爱勇宝3 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
微信开发api-视频号协议4 小时前
企业微信外部群开发自动化实践过程
java·前端·微信·自动化·企业微信·ipad
甲维斯4 小时前
Fable5:20美金的顶级设计师!
前端·人工智能
kyriewen4 小时前
我同时用了 Claude Code、Cursor 和 Codex,说说三个工具的真实差距——2026 年选错工具比不用 AI 更浪费时间
前端·ai编程·claude
Jackson__5 小时前
为了拯救我的腰椎和颈椎,我做了款浏览器插件!
前端·javascript·开源
闲猫5 小时前
Python FastAPI + SQLAlchemy 入门教程:从零搭建你的第一个 Web 应用
前端·python·fastapi
林小帅5 小时前
NestJS v11 + Prisma v7 ESM 迁移配置解析
前端·javascript·后端
IT_陈寒6 小时前
React的setState竟然不是立刻生效的,害我调试半天
前端·人工智能·后端
简简单单就是我_hehe6 小时前
前端埋点日志解决方案:完整 User-Agent 解析工具实现,爬虫识别、设备品牌、操作系统、浏览器版本全解析(附完整源码)
前端·爬虫