vue2,vue3项目无限滚动组件,支持滑动

参数说明

items:项目数组

stepTime:每一步滚动的时间间隔(毫秒)

stepHeight:垂直滚动时每一步的高度

stepWidth:水平滚动时每一步的宽度

threshold:判断是否需要滚动的项目数量阈值

containerHeight:容器的高度

containerWidth:容器的宽度

horizontal:是否水平滚动

fadeInOut:是否启用淡入淡出效果

 

 vue2使用方法 (item为当前数据)

把item结构出来然后在设计样式,使用参考类似饿了吗的table 

        <auto-scroll:items="info":step-time="3000":step-height="220":threshold="1":container-height="220"><template v-slot:default="{ item, index }"><div class="navItem fbc" @click="view(item)">item</div></template></auto-scroll>

vue3使用方法 (同vue2但是结构方法有区别)

<template #default="{ item, index }"><div class="item">{{ index }} - {{ item }}</div>
</template>

vue2源码 

<template><divref="scrollContainer"class="scroll-container":style="containerStyle"@mouseover="handleMouseOver"@mouseleave="handleMouseLeave"@wheel="handleMouseWheel"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"><ul ref="scrollList" class="scroll-list" :style="scrollListStyle"><liv-for="(item, index) in displayItems":key="index":style="itemStyle"><slot :item="item" :index="index">{{ item }}</slot></li></ul></div>
</template><script>
export default {name: "AutoScroll",props: {// 接受的项目数组items: {type: Array,required: true,},// 每一步滚动的时间间隔(毫秒)stepTime: {type: Number,default: 1000,},// 垂直滚动时每一步的高度stepHeight: {type: Number,default: 220,},// 水平滚动时每一步的宽度stepWidth: {type: Number,default: 220,},// 判断是否需要滚动的项目数量阈值threshold: {type: Number,default: 1,},// 容器的高度containerHeight: {type: Number,default: 220,},// 容器的宽度containerWidth: {type: Number,default: 220,},// 是否水平滚动horizontal: {type: Boolean,default: false,},// 是否启用淡入淡出效果fadeInOut: {type: Boolean,default: false,},},data() {return {intervalId: null, // 定时器IDisHover: false, // 鼠标是否悬停在容器上currentPosition: 0, // 当前滚动的位置animationDuration: 1000, // 动画持续时间alignTimeout: null, // 对齐步高的延时器touchStartPosition: 0, // 触摸开始时的位置};},computed: {// 判断是否需要滚动,如果项目数量大于阈值,则需要滚动shouldScroll() {return this.items.length > this.threshold;},// 生成一个显示项目的数组,包含原数组和它的副本,以实现无缝滚动displayItems() {return [...this.items, ...this.items];},// 设置滚动列表的样式,根据是否悬停来决定是否启用过渡动画scrollListStyle() {return {transition: this.isHover ? 'none' : `transform ${this.animationDuration}ms linear`,display: this.fadeInOut ? 'flex' : 'block',opacity: this.fadeInOut ? (this.isHover ? 1 : 0.5) : 1,flexDirection: this.horizontal ? 'row' : 'column',};},// 设置容器样式,确定容器的宽度和高度containerStyle() {return {height: this.horizontal ? 'auto' : `${this.containerHeight}px`,width: this.horizontal ? `${this.containerWidth}px` : 'auto',overflow: 'hidden',position: 'relative',};},// 设置每个项目的样式,确定项目的宽度和高度itemStyle() {return {height: this.horizontal ? 'auto' : `${this.stepHeight}px`,width: this.horizontal ? `${this.stepWidth}px` : 'auto',};},},mounted() {// 组件挂载时初始化滚动设置this.initScrolling();},beforeDestroy() {// 组件销毁前清理滚动设置this.cleanUpScrolling();},watch: {// 监听items属性变化,重新初始化滚动items() {this.initScrolling();},},methods: {// 初始化滚动设置initScrolling() {this.cleanUpScrolling();if (this.shouldScroll && !this.isHover) {this.startScrolling();}},// 开始自动滚动startScrolling() {if (!this.intervalId) {this.intervalId = setInterval(this.scrollStep, this.stepTime);}},// 停止自动滚动stopScrolling() {if (this.intervalId) {clearInterval(this.intervalId);this.intervalId = null;}},// 每步滚动逻辑scrollStep() {const list = this.$refs.scrollList;const maxScrollPosition = this.horizontal? list.scrollWidth / 2: list.scrollHeight / 2;// 如果当前滚动位置超过最大滚动位置,则重置if (this.currentPosition >= maxScrollPosition) {this.currentPosition = 0;list.style.transition = 'none'; // 禁用过渡效果以立即重置位置if (this.horizontal) {list.style.transform = `translateX(0)`;} else {list.style.transform = `translateY(0)`;}setTimeout(() => {list.style.transition = `transform ${this.animationDuration}ms linear`; // 恢复过渡效果this.currentPosition += this.horizontal ? this.stepWidth : this.stepHeight;if (this.horizontal) {list.style.transform = `translateX(-${this.currentPosition}px)`;} else {list.style.transform = `translateY(-${this.currentPosition}px)`;}}, 50);} else {this.currentPosition += this.horizontal ? this.stepWidth : this.stepHeight;if (this.horizontal) {list.style.transform = `translateX(-${this.currentPosition}px)`;} else {list.style.transform = `translateY(-${this.currentPosition}px)`;}}},// 鼠标悬停时停止滚动handleMouseOver() {this.isHover = true;this.stopScrolling();},// 鼠标离开时恢复滚动handleMouseLeave() {this.isHover = false;if (this.shouldScroll) {this.alignToStep(); // 对齐到最近的步高或步宽this.startScrolling();}},// 处理鼠标滚轮事件handleMouseWheel(event) {const list = this.$refs.scrollList;const delta = event.deltaY || event.deltaX;this.currentPosition += delta;if (this.currentPosition < 0) {this.currentPosition = 0;} else if (this.currentPosition >= (this.horizontal ? list.scrollWidth : list.scrollHeight) / 2) {this.currentPosition = (this.horizontal ? list.scrollWidth : list.scrollHeight) / 2 - 1;}list.style.transition = 'none';if (this.horizontal) {list.style.transform = `translateX(-${this.currentPosition}px)`;} else {list.style.transform = `translateY(-${this.currentPosition}px)`;}clearTimeout(this.alignTimeout);this.alignTimeout = setTimeout(this.alignToStep, 100);},// 处理触摸开始事件handleTouchStart(event) {this.touchStartPosition = this.horizontal ? event.touches[0].clientX : event.touches[0].clientY;this.stopScrolling();},// 处理触摸移动事件handleTouchMove(event) {const touchCurrentPosition = this.horizontal ? event.touches[0].clientX : event.touches[0].clientY;const delta = this.touchStartPosition - touchCurrentPosition;this.handleMouseWheel({ deltaY: delta, deltaX: delta });this.touchStartPosition = touchCurrentPosition;},// 处理触摸结束事件handleTouchEnd() {if (this.shouldScroll) {this.alignToStep();this.startScrolling();}},// 对齐到最近的步高或步宽alignToStep() {const list = this.$refs.scrollList;this.currentPosition = Math.round(this.currentPosition / (this.horizontal ? this.stepWidth : this.stepHeight)) * (this.horizontal ? this.stepWidth : this.stepHeight);list.style.transition = `transform ${this.animationDuration}ms linear`;if (this.horizontal) {list.style.transform = `translateX(-${this.currentPosition}px)`;} else {list.style.transform = `translateY(-${this.currentPosition}px)`;}},// 清理滚动设置cleanUpScrolling() {this.stopScrolling();clearTimeout(this.alignTimeout);},},
};
</script><style scoped>
.scroll-container {overflow: hidden;position: relative;
}
.scroll-list {list-style-type: none;margin: 0;padding: 0;display: flex;flex-direction: column;
}
</style>

vue3源码 

 

<template><divref="scrollContainer"class="scroll-container":style="containerStyle"@mouseover="handleMouseOver"@mouseleave="handleMouseLeave"@wheel="handleMouseWheel"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"><ul ref="scrollList" class="scroll-list" :style="scrollListStyle"><liv-for="(item, index) in displayItems":key="index":style="itemStyle"><slot :item="item" :index="index">{{ item }}</slot></li></ul></div>
</template><script setup>
import { ref, onMounted, onBeforeUnmount, computed, watch } from 'vue';// Props定义
const props = defineProps({items: {type: Array,required: true,},stepTime: {type: Number,default: 1000,},stepHeight: {type: Number,default: 220,},stepWidth: {type: Number,default: 220,},threshold: {type: Number,default: 1,},containerHeight: {type: Number,default: 220,},containerWidth: {type: Number,default: 220,},horizontal: {type: Boolean,default: false,},fadeInOut: {type: Boolean,default: false,},
});// 引用DOM元素
const scrollContainer = ref(null);
const scrollList = ref(null);// 定义状态
const intervalId = ref(null);
const isHover = ref(false);
const currentPosition = ref(0);
const animationDuration = ref(1000);
const alignTimeout = ref(null);
const touchStartPosition = ref(0);// 判断是否需要滚动,如果项目数量大于阈值,则需要滚动
const shouldScroll = computed(() => {return props.items.length > props.threshold;
});// 生成一个显示项目的数组,包含原数组和它的副本,以实现无缝滚动
const displayItems = computed(() => {return [...props.items, ...props.items];
});// 设置滚动列表的样式,根据是否悬停来决定是否启用过渡动画
const scrollListStyle = computed(() => {return {transition: isHover.value ? 'none' : `transform ${animationDuration.value}ms linear`,display: props.fadeInOut ? 'flex' : 'block',opacity: props.fadeInOut ? (isHover.value ? 1 : 0.5) : 1,flexDirection: props.horizontal ? 'row' : 'column',};
});// 设置容器样式,确定容器的宽度和高度
const containerStyle = computed(() => {return {height: props.horizontal ? 'auto' : `${props.containerHeight}px`,width: props.horizontal ? `${props.containerWidth}px` : 'auto',overflow: 'hidden',position: 'relative',};
});// 设置每个项目的样式,确定项目的宽度和高度
const itemStyle = computed(() => {return {height: props.horizontal ? 'auto' : `${props.stepHeight}px`,width: props.horizontal ? `${props.stepWidth}px` : 'auto',};
});// 初始化滚动设置
const initScrolling = () => {cleanUpScrolling();if (shouldScroll.value && !isHover.value) {startScrolling();}
};// 开始自动滚动
const startScrolling = () => {if (!intervalId.value) {intervalId.value = setInterval(scrollStep, props.stepTime);}
};// 停止自动滚动
const stopScrolling = () => {if (intervalId.value) {clearInterval(intervalId.value);intervalId.value = null;}
};// 每步滚动逻辑
const scrollStep = () => {const list = scrollList.value;const maxScrollPosition = props.horizontal? list.scrollWidth / 2: list.scrollHeight / 2;// 如果当前滚动位置超过最大滚动位置,则重置if (currentPosition.value >= maxScrollPosition) {currentPosition.value = 0;list.style.transition = 'none'; // 禁用过渡效果以立即重置位置if (props.horizontal) {list.style.transform = `translateX(0)`;} else {list.style.transform = `translateY(0)`;}setTimeout(() => {list.style.transition = `transform ${animationDuration.value}ms linear`; // 恢复过渡效果currentPosition.value += props.horizontal ? props.stepWidth : props.stepHeight;if (props.horizontal) {list.style.transform = `translateX(-${currentPosition.value}px)`;} else {list.style.transform = `translateY(-${currentPosition.value}px)`;}}, 50);} else {currentPosition.value += props.horizontal ? props.stepWidth : props.stepHeight;if (props.horizontal) {list.style.transform = `translateX(-${currentPosition.value}px)`;} else {list.style.transform = `translateY(-${currentPosition.value}px)`;}}
};// 鼠标悬停时停止滚动
const handleMouseOver = () => {isHover.value = true;stopScrolling();
};// 鼠标离开时恢复滚动
const handleMouseLeave = () => {isHover.value = false;if (shouldScroll.value) {alignToStep();startScrolling();}
};// 处理鼠标滚轮事件
const handleMouseWheel = (event) => {const list = scrollList.value;const delta = event.deltaY || event.deltaX;currentPosition.value += delta;if (currentPosition.value < 0) {currentPosition.value = 0;} else if (currentPosition.value >= (props.horizontal ? list.scrollWidth : list.scrollHeight) / 2) {currentPosition.value = (props.horizontal ? list.scrollWidth : list.scrollHeight) / 2 - 1;}list.style.transition = 'none';if (props.horizontal) {list.style.transform = `translateX(-${currentPosition.value}px)`;} else {list.style.transform = `translateY(-${currentPosition.value}px)`;}clearTimeout(alignTimeout.value);alignTimeout.value = setTimeout(alignToStep, 100);
};// 处理触摸开始事件
const handleTouchStart = (event) => {touchStartPosition.value = props.horizontal ? event.touches[0].clientX : event.touches[0].clientY;stopScrolling();
};// 处理触摸移动事件
const handleTouchMove = (event) => {const touchCurrentPosition = props.horizontal ? event.touches[0].clientX : event.touches[0].clientY;const delta = touchStartPosition.value - touchCurrentPosition;handleMouseWheel({ deltaY: delta, deltaX: delta });touchStartPosition.value = touchCurrentPosition;
};// 处理触摸结束事件
const handleTouchEnd = () => {if (shouldScroll.value) {alignToStep();startScrolling();}
};// 对齐到最近的步高或步宽
const alignToStep = () => {const list = scrollList.value;currentPosition.value = Math.round(currentPosition.value / (props.horizontal ? props.stepWidth : props.stepHeight)) * (props.horizontal ? props.stepWidth : props.stepHeight);list.style.transition = `transform ${animationDuration.value}ms linear`;if (props.horizontal) {list.style.transform = `translateX(-${currentPosition.value}px)`;} else {list.style.transform = `translateY(-${currentPosition.value}px)`;}
};// 清理滚动设置
const cleanUpScrolling = () => {stopScrolling();clearTimeout(alignTimeout.value);
};// 组件挂载时初始化滚动设置
onMounted(() => {initScrolling();
});// 组件销毁前清理滚动设置
onBeforeUnmount(() => {cleanUpScrolling();
});// 监听items属性变化,重新初始化滚动
watch(() => props.items, () => {initScrolling();
});
</script><style scoped>
.scroll-container {overflow: hidden;position: relative;
}
.scroll-list {list-style-type: none;margin: 0;padding: 0;display: flex;flex-direction: column;
}
</style>

 

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

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

相关文章

Linux系统配置STM32的开发环境(代码编辑,编译,下载调试)

常见的stm32开发都是直接使用keil-MDK工具的&#xff0c;这是个集成开发环境&#xff0c;包含了代码编辑&#xff0c;编译&#xff0c;下载&#xff0c;调试&#xff0c;等功能&#xff0c;而且keil还是个图形化操作工具&#xff0c;直接可以点击图标案件就可以实现编译下载啥的…

Tableau入门|数据可视化与仪表盘搭建

原视频链接&#xff08;up:戴戴戴师兄&#xff09;&#xff0c;文章为笔者的自学笔记&#xff0c;用于复习回顾&#xff0c;原视频下方有原up整理的笔记&#xff0c;更加直观便捷。因为视频中间涉及的细节较多&#xff0c;建议一边操作&#xff0c;一边学习。 整体介绍 可视化…

set,map(java)

前言&#xff1a;要了解set和map&#xff0c;首先需要对搜索树和哈希有一定的了解&#xff0c;才能进一步深入的了解set和map。 1.搜索树 &#xff08;1&#xff09;性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点值都小于根节点的值。 若它的右子树不…

【单机锁】实现原理

文章目录 1.互斥锁 sync.Mutex 的实现原理&#xff1b;1.1获取策略有如下两种&#xff1a;1.2sync.Mutex的方案1.2.1具体方案如下&#xff1a;1.2.2转换的条件&#xff1a;1.2.3运行的两种模式&#xff1a;1.2.4两种模式的转换条件1.2.5唤醒标识&#xff1a; 1.3源码走读 2. sy…

猫头虎分享:PyTorch异常ModuleNotFoundError: No module named ‘torch’解决方案

&#x1f42f; 猫头虎分享&#xff1a;PyTorch异常ModuleNotFoundError: No module named ‘torch’解决方案 &#x1f4bb; 摘要 在本篇博客中&#xff0c;我们将深入探讨如何解决PyTorch中常见的“ModuleNotFoundError: No module named ‘torch’”错误。通过详细的步骤指…

差分法求解 Burgers 方程(附完整MATLAB 及 Python代码)

Burgers 方程的数值解及误差分析 引言 Burgers 方程是一个非线性偏微分方程&#xff0c;在流体力学、非线性声学和交通流理论中有广泛应用。本文将通过数值方法求解带粘性的 Burgers 方程&#xff0c;并分析其误差。 方程模型 Burgers 方程的形式为&#xff1a; u t u u …

Jmeter下载、安装、永久汉化(Windows环境)

1、JDK下载 JDK8下载地址https://www.oracle.com/java/technologies/downloads/#java8-windows JDK8的Windows的64位&#xff1a; 2、Jmeter下载 jmeter下载地址https://jmeter.apache.org/download_jmeter.cgi 3、配置环境变量 安装好后&#xff0c;把jdk和jmeter都配置到…

Docker从入门到实践教程(电子版)

前言 Docker 是个伟大的项目&#xff0c;它彻底释放了虚拟化的威力&#xff0c;极大降低了云计算资源供应的成本&#xff0c;同时让应用的 分发、测试、部署和分发都变得前所未有的高效和轻松&#xff01; 本电子书既适用于具备基础 Linux 知识的 Docker 初学者&#xff0c;也…

隧道可视化:实时监控保障行车安全

通过图扑可视化实现隧道的实时监控、数据分析及智能报警系统&#xff0c;提供全面的隧道管理和决策支持&#xff0c;提升行车安全&#xff0c;优化维护策略&#xff0c;确保交通顺畅。

【b站-湖科大教书匠】6 应用层 - 计算机网络微课堂

课程地址&#xff1a;【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】 https://www.bilibili.com/video/BV1c4411d7jb/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 6 应用层 6.1 应用层概述 6.2 客户-服务器方式和对等方…

PsExec横向:IPCPTHPTT

一.IPC下的PsExec 二.PTH下的psexec&#xff08;CS操作&#xff09; 三.PTT下的psexec PsExec工具&#xff1a; psexec 是 windows 下非常好的一款远程命令行工具。psexec的使用不需要对方主机开方3389端口&#xff0c;只需要对方开启admin$共享和ipc$ (该共享默认开启&#…

Spring boot 后端向前端发送日期时间发现少了8小时

问题 数据库 后端的控制台输出 前端控制台输出 可以发现少了8小时 问题 springboot 向前端响应数据是默认 Json 格式&#xff0c;所以会有类型转换&#xff0c;springboot 就通过 Jackson 来对 data 类型数据进行转换&#xff0c;但是Jackson 类型的时区是 GMT&#xff0c;与…

Google AI非坦途

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Pytorch框架之神经网络

一、全连接神经网络的整体结构 二、全连接神经网络的单元结构 找出一组w,b使得结果最优 三、常见激活函数 四、前向传播 学习率是指训练模型时每次迭代更新模型参数的步长。 五、梯度下降法 六、反向传播计算 七、总结 1、准备数据 2、搭建模型 3、开始训练(设置学习率、…

【TS】TypeScript中的接口(Interface):对象类型的强大工具

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript中的接口(Interface):对象类型的强大工具引言1. 接口的基本概念1.1 什…

【基于PSINS】UKF/SSUKF对比的MATLAB程序

UKF与SSUKF UKF是&#xff1a;无迹卡尔滤波 SSUKF是&#xff1a;简化超球面无迹卡尔曼滤波 UKF 相较于传统的KF算法&#xff0c;UKF能够更好地处理非线性系统&#xff0c;并且具有更高的估计精度。它适用于多种应用场景&#xff0c;如机器人定位导航、目标跟踪、信号处理等。…

【人工智能】深度剖析:Midjourney与Stable Diffusion的全面对比

文章目录 &#x1f34a;1 如何选择合适的AI绘画工具1.1 个人需求选择1.2 比较工具特点1.3 社区和资源 &#x1f34a;2 Midjourney VS Stable Diffusion&#xff1a;深度对比与剖析 2.1 使用费用对比 2.2 使用便捷性与系统兼容性对比 2.3 开源与闭源对比 2.4 图片质量对比 2.5 上…

19145 最长无重复子数组

这个问题可以使用滑动窗口的方法来解决。我们可以使用两个指针&#xff0c;一个指向子数组的开始&#xff0c;一个指向子数组的结束。然后我们使用一个哈希表来记录每个元素最后出现的位置。当我们遇到一个已经在子数组中出现过的元素时&#xff0c;我们就将开始指针移动到这个…

Mac文件拷贝到移动硬盘怎么做Mac拷贝之后Win电脑里看不到

在日常使用mac电脑的过程中&#xff0c;我们经常需要将一些重要的文件备份到外部硬盘上&#xff0c;以防止数据丢失或电脑故障。传输文件到硬盘可以通过多种方法实现&#xff0c;比如拖拽或者复制至移动硬盘&#xff0c;但有时也会遇到移动硬盘无法粘贴&#xff0c;或拷贝后无法…

SSRF (服务端请求伪造)

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…