基于vue.js的无缝滚动

在这里插入图片描述

方法一:基于requestAnimationFrame

demo

<template><h-page-container class="hoem-page"><h1>无缝滚动</h1><h2>垂直方向</h2><div class="container1"><AutoScroll :data="list" :item-height="110" :limit-move-num="3" :is-rem="false"><template #item="{ keySuffix }"><div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card"><div>{{ item.title }}</div><div>{{ item.content }}</div></div></template></AutoScroll></div><h2>水平方向</h2><div class="container2"><AutoScroll :data="list" :direction="2" :item-width="210" :limit-move-num="3" :is-rem="false"><template #item="{ keySuffix }"><div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card"><div>{{ item.title }}</div><div>{{ item.content }}</div></div></template></AutoScroll></div></h-page-container>
</template><script setup>
import { ref } from 'vue'
import AutoScroll from '@/components/AutoScroll.vue'const list = ref([{id: 1,title: '卡片1',content: '111'},{id: 2,title: '卡片2',content: '222'},{id: 3,title: '卡片3',content: '333'},{id: 4,title: '卡片4',content: '444'}
])</script><style lang="scss" scoped>
.hoem-page {width: 100%;height: 100vh;padding: 10px;
}.container1 {width: 200px;height: 300px;margin: 20px;overflow: hidden;.card {width: 100%;height: 100px;border: 1px solid #e0e0e0;border-radius: 8px;margin-bottom: 10px;padding: 10px;}
}.container2 {width: 500px;height: 150px;margin: 20px;overflow: hidden;.card {width: 200px;height: 100%;border: 1px solid #e0e0e0;border-radius: 8px;margin-right: 10px;padding: 10px;}
}
</style>

AutoScroll.vue

<template><div class="scroll-list" :style="listStyle" @mouseover="pauseAnimation" @mouseout="animate"><slot name="item" key-suffix=""></slot> <!-- 原始内容 --><template v-if="shouldDuplicate"><slot name="item" key-suffix="_copy"></slot> <!-- 复制内容,添加后缀 --></template></div>
</template><script setup>
import { ref, onBeforeUnmount, watch, computed } from 'vue'
// 定义滚动方向枚举
const Direction = {DOWN: 0,UP: 1,LEFT: 2,RIGHT: 3
}const props = defineProps({// 列表data: {type: Array,default: () => []},// 方向: 0 往下 1 往上 2 向左 3 向右direction: {type: Number,default: 1},/*** 一个列表元素的高度(包含外边距)* direction为0 往下 1 往上时*/itemHeight: {type: Number,default: null},/*** 一个列表元素的宽度(包含外边距)* direction为2 向左 3 向右时*/itemWidth: {type: Number,default: null},// itemHeight的单位是否是remisRem: {type: Boolean,default: true},// 开启无缝滚动的数据量。limitMoveNum: {type: Number,default: 5},// 几列columns: {type: Number,default: 1}
})// 是否需要复制内容
const shouldDuplicate = computed(() => props.data.length >= props.limitMoveNum)const requestId = ref(null)
const offset = ref(0)// 判断是否为垂直方向
const isVertical = computed(() => props.direction === Direction.DOWN || props.direction === Direction.UP)// 计算列表样式
const listStyle = computed(() => ({transform: `${isVertical.value ? 'translateY' : 'translateX'}(${offset.value}${props.isRem ? 'rem' : 'px'})`,display: isVertical.value ? 'block' : 'flex'
}))// 计算最大偏移量
const maxOffset = computed(() => {return Math.ceil(props.data.length / props.columns) *(isVertical.value ? props.itemHeight : props.itemWidth)
})// 开始动画
const animate = () => {if (props.data?.length < props.limitMoveNum) returnrequestId.value = requestAnimationFrame(() => {animate()offset.value += (props.direction === Direction.UP || props.direction === Direction.LEFT) ? -0.3 : 0.3// 当滚动完一轮后重置位置if (Math.abs(offset.value) >= maxOffset.value) {offset.value = 0}})
}// 暂停动画
const pauseAnimation = () => {if (requestId.value) {cancelAnimationFrame(requestId.value)requestId.value = null}
}watch(() => props.data, (val) => {if (val?.length >= props.limitMoveNum) {pauseAnimation()animate()}
}, {immediate: true
})onBeforeUnmount(() => {pauseAnimation()
})</script><style lang="scss" scoped>
.scroll-list {width: 100%;height: 100%;/* 确保动画在合成层运行 */backface-visibility: hidden;&>* {flex-grow: 0;flex-shrink: 0;}
}
</style>

**

方法二:基于animation动画

**
demo

<template><h-page-container class="hoem-page"><h1>无缝滚动</h1><h2>垂直方向</h2><div class="container1"><AutoScroll :data="list" :item-height="110" :limit-move-num="3"><template #item="{ keySuffix }"><div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card"><div>{{ item.title }}</div><div>{{ item.content }}</div></div></template></AutoScroll></div><h2>水平方向</h2><div class="container2"><AutoScroll :data="list" :direction="2" :item-width="210" :limit-move-num="3"><template #item="{ keySuffix }"><div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card"><div>{{ item.title }}</div><div>{{ item.content }}</div></div></template></AutoScroll></div></h-page-container>
</template><script setup>
import { ref } from 'vue'
import AutoScroll from '@/components/AutoScroll.vue'const list = ref([{id: 1,title: '卡片1',content: '111'},{id: 2,title: '卡片2',content: '222'},{id: 3,title: '卡片3',content: '333'},{id: 4,title: '卡片4',content: '444'}
])</script><style lang="scss" scoped>
.hoem-page {width: 100%;height: 100vh;padding: 10px;
}.container1 {width: 200px;height: 300px;margin: 20px;overflow: hidden;.card {width: 100%;height: 100px;border: 1px solid #e0e0e0;border-radius: 8px;margin-bottom: 10px;padding: 10px;}
}.container2 {width: 500px;height: 150px;margin: 20px;overflow: hidden;.card {width: 200px;height: 100%;border: 1px solid #e0e0e0;border-radius: 8px;margin-right: 10px;padding: 10px;}
}
</style>

AutoScroll.vue

<template><div class="auto-scroll" :class="scrollClass" @mouseenter="setScrollPause(true)" @mouseleave="setScrollPause(false)"><div ref="scrollContent" class="scroll-content" :style="contentStyle"><slot name="item" :key-suffix="''"></slot><template v-if="shouldDuplicate"><slot name="item" :key-suffix="'_copy'"></slot></template></div></div>
</template><script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'const props = defineProps({// 列表data: {type: Array,default: () => []},// 方向: 0 往下 1 往上 2 向左 3 向右direction: {type: Number,default: 0},/*** 一个列表元素的高度(包含外边距)* direction为0 往下 1 往上时*/itemHeight: {type: Number,default: 40},/*** 一个列表元素的宽度(包含外边距)* direction为2 向左 3 向右时*/itemWidth: {type: Number,default: 100},// 开启无缝滚动的数据量。limitMoveNum: {type: Number,default: 5},// 完整滚动周期(秒)duration: {type: Number,default: 10},// 鼠标悬浮暂停hoverPause: {type: Boolean,default: true}
})const scrollContent = ref(null)
const isPaused = ref(false)// 判断是否为垂直方向
const isVertical = computed(() => props.direction <= 1)// 是否需要复制内容
const shouldDuplicate = computed(() => props.data.length >= props.limitMoveNum)// 滚动方向
const scrollClass = computed(() => [`direction-${props.direction}`,isVertical.value ? 'vertical' : 'horizontal'
])// 内容样式计算
const contentStyle = computed(() => {const sizeProp = isVertical.value ? 'height' : 'width'const itemSize = isVertical.value ? props.itemHeight : props.itemWidthconst contentSize = props.data.length * itemSizereturn {[sizeProp]: shouldDuplicate.value ? `${contentSize * 2}px` : `${contentSize}px`,'animation-duration': `${props.duration}s`,'animation-play-state': isPaused.value ? 'paused' : 'running'}
})// 动画控制
const setScrollPause = (paused) => {if (props.hoverPause) {isPaused.value = paused}
}// 生命周期
onMounted(() => {if (shouldDuplicate.value) {setScrollPause(false)}
})onBeforeUnmount(() => {setScrollPause(true)
})
</script><style scoped>
.auto-scroll {width: 100%;height: 100%;overflow: hidden;position: relative;
}.scroll-content {position: absolute;will-change: transform;animation-timing-function: linear;animation-iteration-count: infinite;
}/* 垂直方向样式 */
.vertical .scroll-content {width: 100%;
}/* 水平方向样式 */
.horizontal .scroll-content {height: 100%;display: flex;
}/* 方向0: 往下 */
.direction-0 .scroll-content {top: 0;animation-name: scrollDown;
}/* 方向1: 往上 */
.direction-1 .scroll-content {bottom: 0;animation-name: scrollUp;
}/* 方向2: 向左 */
.direction-2 .scroll-content {left: 0;animation-name: scrollLeft;
}/* 方向3: 向右 */
.direction-3 .scroll-content {right: 0;animation-name: scrollRight;
}@keyframes scrollDown {0% {transform: translateY(0);}100% {transform: translateY(-50%);}
}@keyframes scrollUp {0% {transform: translateY(0);}100% {transform: translateY(50%);}
}@keyframes scrollLeft {0% {transform: translateX(0);}100% {transform: translateX(-50%);}
}@keyframes scrollRight {0% {transform: translateX(0);}100% {transform: translateX(50%);}
}
</style>

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

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

相关文章

【Linux学习|黑马笔记|Day4】IP地址、主机名、网络请求、下载、端口、进程管理、主机状态监控、环境变量、文件的上传和下载、压缩和解压

【DAY4】 今天看的是Linux第四章剩余部分 至此Linux暂时学到这&#xff0c;第五章还包含很多软件的安装&#xff0c;但是等我要用的时候再装吧 我现在只装了MySQL8.0&#xff0c;具体教程请看笔记安装教程 内容包含更换镜像源和安装配置步骤 文章目录【DAY4】6&#xff09;IP地…

【合新通信】射频光纤传输模块详解

射频光纤传输模块是一种将射频(RF)信号通过光纤进行传输的关键设备&#xff0c;广泛应用于通信、军事、广播电视等领域。以下是关于射频光纤传输模块的全面介绍&#xff1a;基本原理与组成射频光纤传输模块主要由以下几部分组成&#xff1a;电光转换单元&#xff1a;将输入的射…

【信息收集】从GET到POST:破解登录表单的全流程

目标&#xff1a;将浏览器数据代理至BP的proxy模块。将个人PHP的留言板项目首页登录数据包代理至BP&#xff0c;并转发至intrder模块&#xff0c;进行暴力破解。免责声明&#xff1a;本文章内容仅用于个人网络安全知识学习与研究&#xff0c;严禁用于任何未经授权的攻击或非法活…

【办公自动化】如何使用Python操作PPT和自动化生成PPT?

在现代商业和教育环境中&#xff0c;PowerPoint演示文稿是信息传递的重要工具。通过Python自动化PPT创建和编辑过程&#xff0c;可以大幅提高工作效率&#xff0c;特别是在需要批量生成或更新演示文稿的场景下。本文将介绍如何使用python-pptx库实现PPT自动化&#xff0c;并提供…

18 ABP Framework 模块管理

ABP Framework 模块管理 概述 该页面详细介绍了在 ABP Framework 解决方案中使用 ABP CLI 及相关工具添加、更新和管理模块的方法。模块管理是 ABP 模块化架构的核心&#xff0c;支持可重用业务和基础设施功能的集成。模块通常以 NuGet 和/或 NPM 包的形式分发&#xff0c;有时…

外观模式C++

外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它为复杂系统提供一个简化的接口&#xff0c;隐藏系统内部的复杂性&#xff0c;使客户端能够更轻松地使用系统。这种模式通过创建一个外观类&#xff0c;封装系统内部的交互逻辑&#xff0c;客…

Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值

&#x1f31f; Hello&#xff0c;我是蒋星熠Jaxonic&#xff01; &#x1f308; 在浩瀚无垠的技术宇宙中&#xff0c;我是一名执着的星际旅人&#xff0c;用代码绘制探索的轨迹。 &#x1f680; 每一个算法都是我点燃的推进器&#xff0c;每一行代码都是我航行的星图。 &#x…

Java面试宝典:JVM性能优化

一、运行时优化 Java虚拟机(JVM)的运行时优化是提升Java应用性能的核心环节。JIT(Just-In-Time)编译器在程序运行过程中,通过分析热点代码的执行模式,动态地进行一系列高级优化。这些优化技术对开发者透明,但了解其原理能帮助我们编写出更易于优化的代码,从而显著提升…

Day55--图论--107. 寻找存在的路径(卡码网)

Day55–图论–107. 寻找存在的路径&#xff08;卡码网&#xff09; 今天学习并查集。先过一遍并查集理论基础。再做下面这一道模板题&#xff0c;就可以结束了。体量不多&#xff0c;但是理解并查集&#xff0c;并使用好&#xff0c;不容易。 后续再找相关的题目来做&#xff0…

C++中的链式操作原理与应用(三):专注于异步操作延的C++开源库 continuable

目录 1.简介 2.安装与集成 3.快速入门 4.完整示例 5.优势与适用场景 1.简介 continuable 是一个专注于 异步操作延续&#xff08;continuation&#xff09; 的现代 C 开源库&#xff0c;旨在简化异步编程流程&#xff0c;解决 “回调地狱” 问题&#xff0c;提供直观、灵活…

STM32--寄存器与标准库函数--通用定时器--输出比较(PWM生成)

目录 前言 通用定时器类型 向上计数、向下计数、中心对齐 输入捕获与输出比较概念 输出比较典型例子&#xff1a;驱动舵机旋转 通用定时器的输出比较库函数 代码 通用定时器的输出比较寄存器操作 代码 这里提供数据手册的寄存器 后言 前言 使用平台:STM32F407ZET6 使…

91、23种设计模式

设计模式是软件设计中反复出现的解决方案的模板&#xff0c;用于解决特定问题并提高代码的可维护性、可扩展性和可复用性。23种经典设计模式可分为创建型、结构型和行为型三大类&#xff0c;以下是具体分类及模式概述&#xff1a; 一、创建型模式&#xff08;5种&#xff09; 关…

力扣(串联所有单词的子串)

串联所有单词的子串问题&#xff1a;多滑动窗口与哈希表的实战应用。 一、题目分析&#xff08;一&#xff09;问题定义 给定字符串 s 和字符串数组 words&#xff08;words 中所有单词长度相同 &#xff09;&#xff0c;找出 s 中所有“串联子串”的起始索引。串联子串指包含 …

RH134 管理基本存储知识点

1. 对 Linux 磁盘进行分区时有哪两种方案&#xff1f;分别加以详细说明。答&#xff1a;MBR分区&#xff1a;主引导记录(MBR)分区方案是运行BIOS固件的系统上的标准方案。此方案支持最 多四个主分区。在Linux系统上&#xff0c;您可以使用扩展分区和逻辑分区来创建最多…

【JS 异步】告别回调地狱:Async/Await 和 Promise 的优雅实践与错误处理

【JS 异步】告别回调地狱&#xff1a;Async/Await 和 Promise 的优雅实践与错误处理 所属专栏&#xff1a; 《前端小技巧集合&#xff1a;让你的代码更优雅高效 上一篇&#xff1a; 【JS 数组】数组操作的“瑞士军刀”&#xff1a;精通 Array.reduce() 的骚操作 作者&#xff…

23.Linux : ftp服务及配置详解

Linux &#xff1a; ftp服务及配置详解 FTP 基本概念 定义&#xff1a;文件传输协议&#xff08;File Transfer Protocol&#xff09;&#xff0c;采用 C/S 模式工作。端口&#xff1a; 控制端口&#xff1a;21数据端口&#xff1a;20FTP 工作原理模式工作流程连接发起方主动模…

悲观锁乐观锁与事务注解在项目实战中的应用场景及详细解析

在今天做的项目练习部分中真的学到了很多东西&#xff0c;也补充了许多之前遗漏或是忘记的知识点&#xff0c;但时间精力有限&#xff0c;我就先记录一下今天用到的一个新东西&#xff0c;悲观锁和乐观锁。首先给出实际应用背景&#xff1a;在加入锁和事务注解之前&#xff0c;…

Java构造器与工厂模式(静态工程方法)详解

1. 构造器1.1 构造器的核心意义1.1.1 对象初始化构造器在创建对象 (new) 时自动调用, 用于初始化对象的状态 (如设置字段初始值, 分配资源等)无构造器时: 字段为默认值&#xff08;0/null/false&#xff09;有构造器&#xff1a;确保对象创建后即处于有效状态1.1.2 强制初始化…

解决jdk初始化运行,防火墙通信选错专业网络问题

问题描述新项目添加不同版本的jdk&#xff0c;运行时提示防火墙通信策略&#xff0c;选成专用网络。其他人访问后端接口时&#xff0c;提示连接失败。 解决方案&#xff1a;1、在搜索栏中输入 防火墙关键字&#xff0c;选择到防火墙和网络保护2、选择允许应用通过防火墙3、先点…

【Linux】常用命令(三)

【Linux】常用命令&#xff08;三&#xff09;1. export1.1 原理1.2 常用语法1.3 示例1.4 书中对命令的解释1.5 生效范围2. 测试服务地址与其端口能否访问2.1 nc(Netcat)命令2.2 telnet2.3 nmap2.4 curl命令 (适用于HTTP/HTTPS 服务)1. export export 是 Linux Shell&#xff…