循环滚动列表浅析

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

准备条件

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

后记

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

相关推荐
西洼工作室2 分钟前
【解决导航栏字体图标渲染导致文本闪烁问题】采用腾讯视频的解决方案
前端·css·css3
WindrunnerMax10 分钟前
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
前端·架构·github
CodeSheep15 分钟前
宇树科技,改名了!
前端·后端·程序员
Hilaku23 分钟前
为什么我们用了 Vite 还是构建慢?——真正的优化在这几步
前端·javascript·vite
XI锐真的烦23 分钟前
横向对比npm和yarn
前端·npm·node.js
国家不保护废物23 分钟前
🧩 React 组件化进阶:像乐高大师一样搭建你的应用世界!
前端·react.js·ai编程
TimelessHaze30 分钟前
从"切图崽"到前端工程师:React 到底是个啥?🚀
前端·react.js·ai编程
站在风口的猪110835 分钟前
《前端面试题:CSS的display属性》
前端·css·html·css3·html5
wandongle36 分钟前
HTML 面试题错题总结与解析
前端·面试·html