循环滚动列表浅析

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

准备条件

由于存在动态数据,本文为了方便,基于 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>

后记

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

相关推荐
前端涂涂几秒前
Node.js 中的 Buffer(缓冲区)
前端
糖墨夕10 分钟前
【2】Three.js-创建3D场景
前端·webgl·three.js
三原14 分钟前
什么是微应用?我需不需要使用微应用?
前端·架构·设计
三原17 分钟前
前端微应用-乾坤(qiankun)原理分析-single-spa
前端·架构·设计
布兰妮甜26 分钟前
Angular 框架详解:从入门到进阶
前端·javascript·前端框架·angular.js
独立开阀者_FwtCoder36 分钟前
做Docx预览,一定要做这个神库!!
前端·javascript·面试
独立开阀者_FwtCoder38 分钟前
搞定 XLSX 预览?别瞎找了,这几个库(尤其最后一个)真香!
前端·javascript·面试
杯莫停丶1 小时前
Web Worker在uniapp鸿蒙APP中的深度应用
前端·uni-app
小小小小宇1 小时前
重新探讨React Diff算法
前端
excel1 小时前
webpack 模块图 第 五 节
前端