vue3实现一个无缝衔接、滚动平滑的列表自动滚屏效果,支持鼠标移入停止移出滚动

文章目录

  • 前言
  • 一、滚动元素相关属性回顾
  • 一、实现分析
  • 二、代码实现
    • 示例:
    • 2、继续添加功能,增加鼠标移入停止滚动、移出继续滚动效果
    • 2、继续完善


前言

列表自动滚屏效果常见于大屏开发场景中,本文将讲解用vue3实现一个无缝衔接、滚动平滑的列表自动滚屏效果,并支持鼠标移入停止滚动、移出自动滚动。


一、滚动元素相关属性回顾

在这里插入图片描述

scrollHeight:滚动元素总高度,包括顶部被隐藏区域高度+页面可视区域高度+底部未显示区域高度
scrollTop:滚动元素顶部超出可视区域的高度,通过改变该值可以控制滚动条位置

一、实现分析

1、如何让滚动条自动滚动?

scrollTop属性表示滚动元素顶部与可视区域顶部距离,也即滚动条向下滚动的距离,只要设置一个定时器(setInterval)相同增量改变scrollTop值就能匀速向下滚动

2、如何做到滚动平滑无缝衔接?

无缝衔接要求滚动到最后一个数据下面又衔接上从头开始的数据造成一种无限滚屏假象,平滑要求从最后一数据衔接上首个数据不能看出滚动条或页面有跳动效果。实现方案可以多复制一份列表数据追加在原数据后面,当滚动到第一份数据的末尾由于后面还有复制的数据,滚动条依然可以向下滚动,直到第一份数据最后一个数据滚出可视区域再把滚动条重置到初始位置(scrollTop=0),此时从第二份数据首个位置变到第一份数据的首个位置由于页面数据一样视觉效果上看将感觉不到页面的滚动和变化,所以就能得到平滑无缝衔接效果。

二、代码实现

示例:

demo.vue

<template><div class="page"><div class="warning-view"><div class="label">预警信息</div><divclass="scroll-view"ref="scrollViewRef"><div ref="listRef" class="list" v-for="(p, n) in 2" :key="n"><div class="item" v-for="(item, index) in data" :key="index"><div class="content">预警消息 {{ index }}</div><div class="time">2024-11-06</div></div></div></div></div></div>
</template><script setup>
import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
const data = ref(); //列表数据
const listRef = ref(); //列表dom
const scrollViewRef = ref(); //滚动区域domlet intervalId = null;//获取列表数据
const getData = () => {//模拟接口请求列表数据return new Promise((resolve, reject) => {setTimeout(() => {//生成10条数据let list = new Array(10).fill().map((item, index) => index);resolve(list);}, 1000);});
};onMounted(async () => {data.value = await getData();nextTick(()=>{autoScrolling()})
});
//设置自动滚动
const autoScrolling = () => {intervalId = setInterval(() => {if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {scrollViewRef.value.scrollTop += 1;} else {scrollViewRef.value.scrollTop = 0;}}, 20);
};onBeforeUnmount(() => {//离开页面清理定时器intervalId && clearInterval(intervalId);
});</script><style scoped>
.page {width: 100%;height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #010c1e;color: #fff;
}
.warning-view {width: 400px;height: 400px;border: 1px solid #fff;display: flex;flex-direction: column;
}
.label {color: #fff;padding: 20px;font-size: 22px;
}
.scroll-view {flex: 1;height: 0;width: 100%;overflow-y: auto;
}
.list {width: 100%;padding: 0 20px;box-sizing: border-box;
}
.item {width: 100%;height: 50px;min-height: 50px;font-size: 16px;display: flex;align-items: center;justify-content: space-between;color: #eee;
}
/**
*隐藏滚动条*/::-webkit-scrollbar{display: none;}
</style>

说明:布局方面 定义了一个可滚动父元素div(scrollViewRef),子元素 通过v-for="(p, n) in 2“ 循环渲染2份相同的列表数据并挂载在2个div(listRef)上,每隔20ms滚动条scrollTop+1,直到滚完第一个列表最后一个数据出了屏幕,滚动条重新回到初始位置。通过scrollViewRef.value.scrollTop < listRef.value[0].clientHeight判断。

运行效果:
(ps:由于视频转gif帧率变小造成看起来有些卡顿,实际滚动效果非常丝滑)

请添加图片描述

2、继续添加功能,增加鼠标移入停止滚动、移出继续滚动效果

demo.vue

<template><div class="page"><div class="warning-view"><div class="label">预警信息</div><divclass="scroll-view"ref="scrollViewRef"@mouseenter="onMouseenter"@mouseleave="onMouseleave"><div ref="listRef" class="list" v-for="(p, n) in 2" :key="n"><div class="item" v-for="(item, index) in data" :key="index"><div class="content">预警消息 {{ index }}</div><div class="time">2024-11-06</div></div></div></div></div></div>
</template><script setup>
import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
const data = ref(); //列表数据
const listRef = ref(); //列表dom
const scrollViewRef = ref(); //滚动区域domlet intervalId = null;
let isAutoScrolling = true; //是否自动滚动标识//获取列表数据
const getData = () => {//模拟接口请求列表数据return new Promise((resolve, reject) => {setTimeout(() => {//生成10条数据let list = new Array(10).fill().map((item, index) => index);resolve(list);}, 1000);});
};onMounted(async () => {data.value = await getData();nextTick(() => {autoScrolling();});
});//设置自动滚动
const autoScrolling = () => {intervalId = setInterval(() => {if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;} else {scrollViewRef.value.scrollTop = 0;}}, 20);
};onBeforeUnmount(() => {//离开页面清理定时器intervalId && clearInterval(intervalId);
});//鼠标进入,停止滚动
const onMouseenter = () => {isAutoScrolling = false;
};
//鼠标移出,继续滚动
const onMouseleave = () => {isAutoScrolling = true;
};
</script><style scoped>
.page {width: 100%;height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #010c1e;color: #fff;
}
.warning-view {width: 400px;height: 400px;border: 1px solid #fff;display: flex;flex-direction: column;
}
.label {color: #fff;padding: 20px;font-size: 22px;
}
.scroll-view {flex: 1;height: 0;width: 100%;overflow-y: auto;
}
.list {width: 100%;padding: 0 20px;box-sizing: border-box;
}
.item {width: 100%;height: 50px;min-height: 50px;font-size: 16px;display: flex;align-items: center;justify-content: space-between;color: #eee;
}
/**
*隐藏滚动条*/::-webkit-scrollbar{display: none;}
</style>

说明:定义一个全局变量isAutoScrolling标识鼠标是否移入,当鼠标移入isAutoScrolling为false,scrollTop增量为0,当鼠标移出scrollTop增量恢复到1,scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;

运行效果:(ps:由于视频转gif帧率变小造成看起来有些卡顿,实际滚动效果非常丝滑)

请添加图片描述

2、继续完善

上面示例都是默认数据较多会出现滚动条情况下实现的,实际开发过程列表数据是不固定的,可能很多条也可能很少无法超出屏幕出现滚动条,比如列表只有一条数据情况下是不需要自动滚动的,这时候如果强制v-for="(p, n) in 2“ 复制一份数据页面会渲染2条一样数据而且无法出现滚动条,所以正确做法还需要动态判断列数是否会出现滚动条,满足出现的条件才去设置自动滚屏。

是否出现滚动条判断:滚动区域高度>自身可见区域高度(scrollHeight > clientHeight)说明有滚动条

完整代码示例:
demo.vue

<template><div class="page"><div class="warning-view"><div class="label">预警信息</div><divclass="scroll-view"ref="scrollViewRef"@mouseenter="onMouseenter"@mouseleave="onMouseleave"><div ref="listRef" class="list" v-for="(p, n) in count" :key="n"><div class="item" v-for="(item, index) in data" :key="index"><div class="content">预警消息 {{ index }}</div><div class="time">2024-11-06</div></div></div></div></div></div>
</template><script setup>
import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
const data = ref(); //列表数据
const listRef = ref(); //列表dom
const scrollViewRef = ref(); //滚动区域dom
const count = ref(1); //列表个数let intervalId = null;
let isAutoScrolling = true; //是否自动滚动标识//获取列表数据
const getData = () => {//模拟接口请求列表数据return new Promise((resolve, reject) => {setTimeout(() => {//生成10条数据let list = new Array(10).fill().map((item, index) => index);resolve(list);}, 1000);});
};onMounted(async () => {data.value = await getData();nextTick(() => {//判断列表是否生成滚动条count.value = hasScrollBar() ? 2 : 1;//有滚动条开始自动滚动if (count.value == 2) {autoScrolling();}});
});
//判断列表是否有滚动条
const hasScrollBar = () => {return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
};
//设置自动滚动
const autoScrolling = () => {intervalId = setInterval(() => {if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;} else {scrollViewRef.value.scrollTop = 0;}}, 20);
};onBeforeUnmount(() => {//离开页面清理定时器intervalId && clearInterval(intervalId);
});//鼠标进入,停止滚动
const onMouseenter = () => {isAutoScrolling = false;
};
//鼠标移出,继续滚动
const onMouseleave = () => {isAutoScrolling = true;
};
</script><style scoped>
.page {width: 100%;height: 100vh;display: flex;justify-content: center;align-items: center;background-color: #010c1e;color: #fff;
}
.warning-view {width: 400px;height: 400px;border: 1px solid #fff;display: flex;flex-direction: column;
}
.label {color: #fff;padding: 20px;font-size: 22px;
}
.scroll-view {flex: 1;height: 0;width: 100%;overflow-y: auto;
}
.list {width: 100%;padding: 0 20px;box-sizing: border-box;
}
.item {width: 100%;height: 50px;min-height: 50px;font-size: 16px;display: flex;align-items: center;justify-content: space-between;color: #eee;
}
/*隐藏滚动条*/::-webkit-scrollbar{display: none;}
</style>

运行效果:
在这里插入图片描述
把数据改成只有一条

//获取列表数据
const getData = () => {//模拟接口请求列表数据return new Promise((resolve, reject) => {setTimeout(() => {//生成1条数据let list = new Array(1).fill().map((item, index) => index);resolve(list);}, 1000);});
};

运行效果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/6086.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vscode翻译插件

vscode翻译插件 需求 &#xff1a; 在编写代码的时候&#xff0c; 打印或者定义变量的时候总是想不起来英文名称&#xff0c; 所有就开发了一款中文转换为英文的插件。 功能 1、目前支持选中中文&#xff0c;右键选择打印或者变量进行转换。 2、目前支持选中中文&#xff0…

Linux -- 初识线程

目录 线程的初步认识 为什么需要线程 怎么让代码分成多个执行流并发执行呢&#xff1f; 管理线程 线程的初步认识 线程是进程内部的一个执行分支&#xff0c;线程是CPU调度的基本单位。 在Linux操作系统中&#xff0c;线程是程序执行流的最小单位。一个进程可以包含多个线…

基于IM场景下的Wasm初探:提升Web应用性能|得物技术

一、何为Wasm &#xff1f; Wasm&#xff0c;全称 WebAssembly&#xff0c;官网描述是一种用于基于堆栈的虚拟机的二进制指令格式。Wasm被设计为一个可移植的目标&#xff0c;用于编译C/C/Rust等高级语言&#xff0c;支持在Web上部署客户端和服务器应用程序。 Wasm 的开发者参…

Linux SSH免密登入以及配置脚本

一、ssh原理简单介绍 客户端生成一对公钥和私钥&#xff0c;并将自己的公钥发送到服务器上 其中公钥用来加密&#xff0c;私钥用来解密。 二、ssh免密登入实现步骤详解 我这就以服务器controller和客户端compute来做为例子 2.1、首先在controller上输入ssh-keygen -t rsa …

Flutter 获取照片权限的时候是否要获取存储权限?

获取存储权限 Permission.storage.request(); 获取照片权限通常意味着访问相册&#xff0c;而访问相册可能还需要外部存储权限&#xff0c;因为照片通常存储在设备的外部存储中。所以&#xff0c;当你请求照片权限时&#xff0c;你也需要检查并请求外部存储权限。 是不是所有…

一个基于Rust适用于 Web、桌面、移动设备等的全栈应用程序框架

大家好&#xff0c;今天给大家分享一个用 Rust 语言编写的、受 React 启发的前端框架Dioxus&#xff0c;旨在为构建跨平台的用户界面提供高效、高性能的解决方案。 项目介绍 Dioxus项目的诞生源于开发者们对于更高效、更灵活的跨平台UI解决方案的渴望。 随着技术的发展&#…

IntelliJ Idea设置自定义快捷键

我IDEA的快捷键是自己修改成了和Eclipse相似&#xff0c;然后想要跳转到某个方法的上层抽象方法没有对应的快捷键&#xff0c;IDEA默认的是Ctrl U &#xff08;Windows/Linux 系统&#xff09; 或 Command U &#xff08;Mac 系统&#xff09;&#xff0c;但是我的不起作用&a…

前深度学习时代-经典的推荐算法

参考自《深度学习推荐系统》—— 王喆&#xff0c;用于学习记录。 1.协同过滤 “协同过滤”就是协同大家的反馈、评价和意见一起对海量的信息进行过滤&#xff0c;从中筛选出目标用户可能感兴趣的信息的推荐过程。 基于用户相似度进行推荐的协同过滤算法 UserCF 用户相似度…

达梦数据守护集群_动态增加实时备库

目录 1、概述 2、实验环境 2.1环境信息 2.2配置信息 2.3 查看初始化参数 3、动态增加实时备库 3.1数据准备 3.2配置新备库 3.3动态增加MAL配置 3.4 关闭守护进程及监视器 3.5修改归档&#xff08;方法1&#xff1a;动态添加归档配置&#xff09; 3.6 修改归档&…

6.qsqlquerymodel源码分析

目录 继承关系入口浅析qsqlquery刷新数据 扩展列或者移除列以及取别名读取数据与增减行读取数据 下一章节&#xff1a;如何使用qsqlquerymodel 与 qtableview实现自定义表格 继承关系 qsqlquerymodel 继承与qabstracttablemodel 入口 负责填充数据 void QSqlQueryModel::s…

【JavaEE】认识线程

一、引入线程 在任务管理器界面可以看到很多进程&#xff0c;可以利用CPU多核心这一点来更有效的执行这些进程&#xff0c;即在处理过程中将CPU的多个时间片分配给每个进程&#xff0c;这样的 "多进程编程"就可以起到并发编程的效果&#xff0c;因为进程可以被调度到…

qt QStatusBar详解

1、概述 QStatusBar是Qt框架提供的一个小部件&#xff0c;用于在应用程序窗口底部显示状态信息。它可以显示一些固定的文本和图标&#xff0c;并且可以通过API动态更新显示内容。QStatusBar通常是一个水平的窗口部件&#xff0c;能够显示多行文本内容&#xff0c;非常适合用于…

【Ubuntu】ubuntu 22.04 设置 Xorg 弃用 Wayland

# 编辑gdm3配置文件 sudo vim /etc/gdm3/custom.conf # 重启gdm3 sudo systemctl restart gdm3方式一&#xff1a;如果进行上述操作后&#xff0c;有对比度严重错误问题&#xff0c;请执行下列命令 # 安装驱动 sudo ubuntu-drivers autoinstall 方式二&#xff1a;推荐尝试方式…

日语学习的难易程度

日语学习的难易程度是一个相对主观的问题&#xff0c;它受到多种因素的影响&#xff0c;包括个人的语言学习能力、学习方法、学习时间、学习资源的可获得性以及个人对日语文化的兴趣和投入程度等。以下是对日语学习难易程度的一些分析&#xff1a; 优点与易学之处 文字系统&am…

WPF 打包

打包为单个exe文件直接运行 - - -版本.NET8 新建WPF项目 右键 - 发布 选择发布文件夹 选择发布文件夹 选择发布文件夹 配置 配置,保存 发布 WPF 打包为exe安装程序 示例 实现思路 引导项目中嵌入其它项目可运行目录的zip引导项目中解压zip文件到指定文件夹是…

RTC精度及校准

RTC精度偏差&#xff1a; RTC的基准时间和精度与石英晶体的频率相关&#xff0c;晶体的谐振频率取决于温度&#xff0c;因此RTC性能与温度相关&#xff0c;晶体的频率偏差是晶体正常频率的温度反转函数。 一、硬件方面&#xff1a; 1.使用高精度振荡器的RTC模块&#xff1b; …

C++ Qt6 QtQuick/QML入门进阶与项目实战视频教程

课程介绍 C Qt这些年在PC客户端、嵌入式、汽车座舱仪表等领域应用很广泛&#xff0c;例如剪映专业版、微信4.0、亿图脑图、Steam、美图秀秀、腾讯会议、钉钉(部分模块)等都是使用C QWidget/QML开发。特别是QML这种声明式UI开发更加快捷&#xff0c;QML的界面开发效率相对于QWid…

Linux:防火墙和selinux对服务的影响

1-1selinux 1-1 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序&#xff0c;因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 1-2 SELinux是通过MAC的方式来控制管理进程&#xff0c;它控制的主体是进程&#xff0c;而目标则是…

逻辑回归处理非线性关系与支持向量机的性能对比

逻辑回归是一种常用的线性分类方法&#xff0c;通常用于处理线性关系的二分类任务。但是&#xff0c;对于非线性问题&#xff0c;传统的逻辑回归模型可能表现不佳&#xff0c;因为它假设数据可以被一个线性决策边界分割开来。为了使逻辑回归能够处理非线性关系&#xff0c;我们…

2021数二第9题 线代 一个方程组的解是另一个方程组的解

题目展示的是一个方程组的列向量 可由另一个列向量表示所以是矩阵的转置