循环滚动列表浅析

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

准备条件

由于存在动态数据,本文为了方便,基于 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 去定时改变它,但我们不想要这么做,所以我们继续改造,使用 css的 animation去实现,由于列表长度不固定,我们需要用到css变量。

首先定义动画,--anim-max-y为y方向最大平移距离,--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>

后记

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

相关推荐
zach3 小时前
前端落地实战:Next.js 项目 Nginx + PM2 生产部署全流程(附踩坑总结)
前端·pm2·next.js
overmind3 小时前
oeasy教h5前端001 修改的乐趣_直接修改在线网页
前端
只睡四小时4 小时前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭4 小时前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
庄园特聘拆椅狂魔4 小时前
从连连看到粒子消散——前端小游戏与视觉特效的技术选型
前端
JudithHuang4 小时前
React 常用 Hooks
前端·react.js·前端框架
sycmancia5 小时前
Qml——Window元素使用
前端
每天都好困5 小时前
03 AR 投影与误差修正:把地图上的点贴到视频画面上
前端
每天都好困5 小时前
04 地图图层封装:像管理 Photoshop 图层一样管理地图元素
前端
去伪存真5 小时前
构建你的第一个 DevOps AI Agent:自动化捕获、分析并修复 CI 故障
前端·agent