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.mzph.cn/news/885139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三周精通FastAPI:36 OpenAPI 回调

官方文档&#xff1a; OpenAPI 回调 您可以创建触发外部 API 请求的路径操作 API&#xff0c;这个外部 API 可以是别人创建的&#xff0c;也可以是由您自己创建的。 API 应用调用外部 API 时的流程叫做回调。因为外部开发者编写的软件发送请求至您的 API&#xff0c;然后您的…

深入解析语音识别中的关键技术:GMM、HMM、DNN和语言模型

目录 一、高斯混合模型&#xff08;GMM&#xff09;与期望最大化&#xff08;EM&#xff09;算法二、隐马尔可夫模型&#xff08;HMM&#xff09;三、深度神经网络&#xff08;DNN&#xff09;四、语言模型&#xff08;LM&#xff09;五、ASR系统的整体工作流程结论 在现代语音…

如何将现有VUE项目所有包更新到最新稳定版

更新有风险,Enter要谨慎!!! 要将项目中的所有 npm 包更新到最新稳定版&#xff0c;可以使用 npm-check-updates 工具。以下是具体步骤&#xff1a; 步骤一&#xff1a;安装 npm-check-updates 首先&#xff0c;全局安装 npm-check-updates 工具&#xff1a; npm install -g…

如何使用 C# 编写一个修改文件时间属性的小工具?

下面是简鹿办公一个用 C# 编写的简单工具&#xff0c;它可以批量修改文件的创建时间、最后访问时间和最后修改时间。我们将使用 .NET Framework 或 .NET Core 来实现这个功能。 完整示例代码 1. 创建一个新的 C# 控制台应用程序 您可以使用 Visual Studio 或 .NET CLI 创建一个…

使用PyQt5设计一个简易计算器

目录 设计UI图 代码 结果展示 设计UI图 代码 from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import QFileDialog, QMainWindow, QMessageBox from untitled import Ui_MainWindow import sysclass…

音频模型介绍

在处理音频数据方面&#xff0c;有多种模型表现出色&#xff0c;它们在不同的音频处理任务上有着各自的优势&#xff1a; 自动编码器&#xff1a;包括多通道变分自动编码器、自回归模型和生成对抗网络等&#xff0c;这些模型在音乐生成领域取得了令人印象深刻的成果。 深度生成…

云计算基础:AWS入门指南

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 云计算基础&#xff1a;AWS入门指南 云计算基础&#xff1a;AWS入门指南 云计算基础&#xff1a;AWS入门指南 引言 AWS概述 什么…

动态规划-两个数组的dp问题——712.两个字符串的最小ASCII删除和

1.题目解析 题目来源 712.两个字符串的最小ASCII删除和——力扣 测试用例 2.算法原理 1.状态表示 由于如果直接求本题会发现无从下手&#xff0c;不妨根据正难则反的原理&#xff0c;反向求公共子序列的ASCII码最大值即可&#xff0c;于是就转化为求公共子序列的问题&#x…

elementui中的新增弹窗在新增数据成功后再新增 发现数据无法清除解决方法

elementui中的新增弹窗在新增数据成功后再新增 发现数据无法清除解决方法 试过网上其他方法&#xff0c;发现表单清空数据还是有问题&#xff0c;索性用下面方法解决: // 给弹框里面添加 v-ifvisible测试无问题&#xff0c;暂时先这样解决&#xff0c;如果有其他方法&#x…

python 天气数据可视化

Python爬取天气数据及可视化分析 https://blog.csdn.net/weixin_69423932/article/details/135184643

基于Arduino的RGB灯按键控制

一.简介 通过按键控制RGB灯分别显示7种颜色&#xff1a;红 、绿、 蓝、 黄、 青、 紫、 白。 二.按键控制RGB灯原理 1)RGB全彩LED: LED由三个颜色分别为&#xff1a;红&#xff08;Red&#xff09;、绿&#xff08;Green&#xff09;、蓝&#xff08;Blue&#xff09;的LED…

非关系型数据库(1)---MongoDB

目录 1.MongoDB 1.MongoDB的特点 2.MongoDB的应用场景 3.MongoDB与MySQL的比较 2.数据库操作 1. 创建数据库 2. 切换数据库 3. 查看所有数据库 4. 查看当前数据库 5. 删除数据库 6. 查看数据库统计信息 7. 备份数据库 8. 恢复数据库 9. 创建用户和授权 10. 删除用…

1. pytorch 中冻结模型参数后参数仍会被调整

问题 self.sgf_net.requires_grad_(False)起初设置 requires_grad(False) 优化器也没有添加sgfnet的模型参数。但是在pylightning框架中&#xff0c;每次推理完模型的参数都会被改变&#xff0c;经过仔细排查发现问题 # self.sgf_net.requires_grad_(False)for param in self.s…

大模型-微调与对齐-人类对齐背景与标准

1、目的 确保大模型的行为与人类价值观、人类真实意图和社会伦理相一致 2、大模型有害行为 无法正确遵循指令生成虚假信息产生有害、有误导性、有偏见的表达 3、评估标准 有用性诚实性无害性 4、更细化的对齐标准 行为对齐&#xff1a;要求AI能够做出符合人类期望的行为…

hive数据查询语法

思维导图 基本查询 基本语法 SELECT [ALL | DISTINCT] 字段名, 字段名, ... FROM 表名 [inner | left outer | right outer | full outer | left semi JOIN 表名 ON 关联条件 ] [WHERE 非聚合条件] [GROUP BY 分组字段名] [HAVING 聚合条件] [ORDER BY 排序字段名 asc | desc…

Docker lmdeploy 快速部署Qwen2.5模型openai接口

启动服务 我已经把模型下载到/data/xiedong/Qwen2.5-72B-Instruct-GPTQ-Int4 所以docker直接启动: docker run --runtime nvidia --gpus device=0 \-p 23333:23333 \--ipc=host -v /data/xiedong:/data/xiedong/ \openmmlab/lmdeploy:latest \lmdeploy serve api_server /d…

前端UniApp面试题及参考答案(100道题)

目录 UniApp 支持哪些平台? UniApp 在不同平台上的表现有何差异? 如何处理 UniApp 中的平台差异? UniApp 项目创建与目录结构 项目创建 目录结构 如何创建一个 UniApp 项目? UniApp 项目的基本目录结构是什么样的? 解释一下 UniApp 中的页面生命周期钩子函数有哪…

前端小知识:我居然没学会用 split 方法?!

小伙伴们&#xff0c;你们会用 JavaScript 的 split 方法吗&#xff1f;最近我才发现&#xff0c;原来我多年来一直没真正掌握它&#xff0c;结果在解题时被卡住了。所以今天&#xff0c;我决定好好整理一下这个方法的用法。 在讨论问题之前&#xff0c;先来看一下 split 的两种…

Java教学新动力:SpringBoot辅助平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理教学辅助平台的相关信息成为必然。开发合适…

【Rust Crate之Actix Web(一)】

Rust Crate之Actix Web 什么是Actix Web&#xff1f;Actix Web 入门代码宏展开,看看 #[get("/")] 做了什么Actix Web中的StateActix Web中的scopeActix Web中的extractorsPathQueryJSONURL-encoded form 总结 什么是Actix Web&#xff1f; Actix Web is a poweful &…