当前位置: 首页 > news >正文

基于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.lryc.cn/news/620405.html

相关文章:

  • 系统设计——DDD领域模型驱动实践
  • rustdesk 开源遥控软件
  • 【深度学习计算性能】04:硬件
  • 医疗AI问答系统实战:知识图谱+大模型的融合应用开发
  • Trae x Figma MCP一键将设计稿转化为精美网页
  • 【python】类型注解
  • CICD-Devops整合Kubernetes-4
  • 深入学习Autosar之BswM模块
  • 4.2 Vue3中reactive与ref详解及区别
  • 云计算-多服务集群部署实战指南:从JumpServer到Kafka、ZooKeeper 集群部署实操流程
  • 命名空间——网络(net)
  • 4.1vue3的setup()
  • EtherCAT概念介绍
  • 防抖 debounce.js
  • Synology File Station 官方 API 指南总结(中文版)
  • windows 资源管理器缩略图 ,支持.MP4(H.265/HEVC编码)视频格式和.HEIC(HEIF)图片格式的软件
  • 《吃透 C++ 类和对象(中):拷贝构造函数与赋值运算符重载深度解析》
  • Cypher注入详解:原理、类型与测试方法
  • Python入门第1课:环境搭建与第一个程序“Hello World”
  • SQL详细语法教程(三)mysql的函数知识
  • Mac 新电脑安装cocoapods报错ruby版本过低
  • 计算机如何进行“卷积”操作:从图像到矩阵的奥秘
  • Java进阶学习之Stream流的基本概念以及使用技巧
  • OS设备UDID查看方法
  • Java毕业设计选题推荐 |基于SpringBoot的健身爱好线上互动与打卡社交平台系统 互动打卡小程序系统
  • UniVoc:基于二维矩阵映射的多语言词汇表系统
  • 机海沉浮录,荣耀的HTC式困局
  • 重塑隐私边界,微算法科技(NASDAQ:MLGO)开发基于边缘计算的轻量级区块链身份隐私保护方案
  • 【论文阅读 | CVPR 2024 | UniRGB-IR:通过适配器调优实现可见光-红外语义任务的统一框架】
  • 【C++】细说继承(2w字详解)