Vue组件封装之无限滚动列表
无限滚动列表:分为单步滚动和循环滚动两种方式
<template>
<div class="box" :style="{width:widthX,height:heightY}"
@mouseenter="mEnter" @mouseleave="mLeave"
>
<div
class="indefiniteScroll"
:style="{width:widthX,height:heightY,transform:`translateY(${top+'px'})`}"
>
<slot></slot>
</div>
<div
v-if="isFull"
class="indefiniteScroll"
:style="{width:widthX,height:heightY,transform:`translateY(${top2+'px'})`}"
>
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
watch,
onUnmounted,
onMounted,
reactive,
} from "vue";
export default defineComponent({
props:{
width:{ // 盒子宽
type: [Number,String],
default: '400'
},
height:{ // 盒子高
type: [Number,String],
default: '300'
},
scrollList: { // 数据列表
type: Array,
default: []
},
direction:{ // 滚动方向 top | bottom
type: String,
defauilt: 'top'
},
moveType:{ // 滚动类型,0:默认,1:单步停顿
type: [Number,String],
default: 0
},
speed:{ // 速度1-5
type: [Number,String],
default: 1
},
pauseTime:{ // 停顿时间
type: [Number,String],
default: 300
},
singleHeight:{
// 单行高度
type: [Number,String],
default: 30
}
},
setup(props,context){
let widthX:any = ref('')
let heightY:any = ref('')
let top:any = ref('0')
let top2:any = ref('0')
let timer:any = ref(null)
let dis:any = ref(0)
let options:any = reactive({
direction: 'top',
moveType: 0, // 0默认滚动,1单步停顿
speed: 1,
})
let isFull = ref(true) // 数据是否充满盒子
let isIn = false
onMounted(()=>{
methods.getXY()
methods.setOption()
if(Number(props.singleHeight)*props.scrollList.length<=Number(props.height)){
// 如果传入的数据没有占满盒子就不滚动
isFull.value = false
return
} else {
isFull.value = true
methods.scroll('','')
}
})
watch(()=>props.scrollList,()=>{ if(timer) {
window.cancelAnimationFrame(timer)
if(options.direction == 'top') {
top.value = '0'
top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
} else {
top.value = -Number(props.singleHeight)*props.scrollList.length
top2.value = -Number(props.height) // 初始位置
}
}
if(Number(props.singleHeight)*props.scrollList.length<=Number(props.height)){
// 如果传入的数据没有占满盒子就不滚动
isFull.value = false
return
} else {
isFull.value = true
methods.scroll('','')
}
},{
deep:true,
})
onUnmounted(()=>{
if(timer) {
window.cancelAnimationFrame(timer)
}
})
let methods = {
getXY(){ // 盒子宽高
widthX.value = props.width + 'px'
heightY.value = props.height + 'px'
},
setOption(){ // 参数设置
options.direction = props.direction
options.moveType = Number(props.moveType)
if(props.speed<1){ // 限制速度
options.speed = 1
} else if(props.speed>5){
options.speed = 5
} else {
options.speed = Number(props.speed)
}
},
scroll(currentTop:string,currentTop2:string){ // 滚动
if(options.direction == "bottom"){ // 初始位置
if(currentTop){
top.value = currentTop // 鼠标移入移出位置
top2.value = currentTop2 // 初始位置
} else {
top.value = -Number(props.singleHeight)*props.scrollList.length
top2.value = -Number(props.height) // 初始位置
}
} else {
if(currentTop2){
top2.value = currentTop2
} else {
top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
}
}
switch(options.moveType){
case 0:
if(options.direction == "top") {
methods.baseMoveTop()
} else if(options.direction == "bottom"){
methods.baseMoveBottom()
}
break
case 1:
if(options.direction == "top") {
methods.singleMoveTop()
} else if(options.direction == "bottom"){
methods.singleMoveBottom()
}
break
}
},
mEnter(){ // 鼠标移入
if(isFull.value) isIn = true
},
mLeave(){ // 鼠标移出
if(isFull.value){
isIn = false
methods.scroll(top.value,top2.value)
}
},
baseMoveTop(){ // 默认-向上滑动循环
top.value = -options.speed + Number(top.value)// 移动计算
top2.value = -options.speed + Number(top2.value)// 移动计算
if(Number(top.value)<=-Number(props.singleHeight)*props.scrollList.length){
top.value = 0
top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
}
if(!isIn) timer = window.requestAnimationFrame(methods.baseMoveTop)
},
baseMoveBottom(){ // 默认-向下滑动循环
top.value = options.speed + Number(top.value) // 移动计算
top2.value = options.speed + Number(top2.value) // 移动计算
if(Number(top.value)>=0){
top.value = -Number(props.singleHeight)*props.scrollList.length
top2.value = -Number(props.height) // 初始位置
}
if(!isIn) timer = window.requestAnimationFrame(methods.baseMoveBottom)
},
singleMoveTop(){ // 单步-向上滑动循环
// let dir = 1
dis.value = options.speed + dis.value
top.value = -options.speed + Number(top.value) // 移动计算
top2.value = -options.speed + Number(top2.value)// 移动计算
if(Number(top.value)<=-Number(props.singleHeight)*props.scrollList.length){
top.value = 0
top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
}
if(dis.value >= Number(props.singleHeight)){
dis.value = 0
window.cancelAnimationFrame(timer)
let nowTime = 0
let lastTime = Date.now()
function pause() { // 停顿时间计算
nowTime = Date.now()
if(nowTime -lastTime >= Number(props.pauseTime)){
lastTime = nowTime
window.requestAnimationFrame(methods.singleMoveTop)
window.cancelAnimationFrame(timer)
return
}
timer = window.requestAnimationFrame(pause)
}
pause()
return
}
if(!isIn) timer = window.requestAnimationFrame(methods.singleMoveTop)
},
singleMoveBottom(){ // 单步-向下滑动循环
dis.value = Number(options.speed) + dis.value
top.value = options.speed + Number(top.value) // 移动计算
top2.value = options.speed + Number(top2.value) // 移动计算
if(Number(top.value)>=0){
top.value = -Number(props.singleHeight)*props.scrollList.length
top2.value = -Number(props.height) // 初始位置
}
if(dis.value >= Number(props.singleHeight)){ // 滚动一行后停止动画,停顿时间之后继续动画
dis.value = 0
window.cancelAnimationFrame(timer)
let nowTime = 0
let lastTime = Date.now()
function pause() { // 停顿时间计算
nowTime = Date.now()
if(nowTime -lastTime >= Number(props.pauseTime)){
lastTime = nowTime
window.requestAnimationFrame(methods.singleMoveBottom)
window.cancelAnimationFrame(timer)
return
}
timer = window.requestAnimationFrame(pause)
}
pause()
return
}
if(!isIn) timer = window.requestAnimationFrame(methods.singleMoveBottom)
}
} return{
widthX,
heightY,
timer,
options,
dis,
isFull,
top,
top2,
...methods,
}
}
});
</script>
<style lang="postcss" scoped>
.indefiniteScroll{
margin: 0;
padding: 0;
user-select: none;
padding: 1px;
/* transition: all 0.5s; */
.scroll-item{
height: 30px;
line-height: 30px;
font-size: 14px;
color: rgb(0, 0, 0);
p{
margin: 0;
padding: 0;
}
}
.scroll-item:nth-of-type(1){
margin-top: 0;
}
}
.box{
overflow: hidden;
}
</style>
Vue组件封装之无限滚动列表的更多相关文章
- Android 高级UI设计笔记09:Android如何实现无限滚动列表
ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们提供了一个良好,整洁的用户体验 ...
- Android 高级UI设计笔记09:Android实现无限滚动列表
1. 无限滚动列表应用场景: ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们 ...
- 【js】我们需要无限滚动列表吗?
无限滚动列表,顾名思义,是能够无限滚动的列表(愿意是指那些能够不断缓冲加载新数据的列表的).但是,我们真的需要这样一个列表吗?在PC端,浏览器的性能其实已经能够满足海量dom节点的渲染刷新(笔者经过简 ...
- vue组件封装及父子组件传值,事件处理
vue开发中,把有统一功能的部分提取出来,作为一个独立的组件,在需要使用的时候引入,可以有效减少代码冗余.难点在于如果封装,使用,如何传参,派发事件等,我会采取倒叙的方式进行说明.(本文总结于Vue2 ...
- 附件上传vue组件封装(一)
//父页面部分 <attachment @newFileList="newFileList" :operationType="operationType" ...
- vue组件封装选项卡
<template> <myMenu :arr='arr' :arrcontent='content'></myMenu> </template> &l ...
- Vue 组件封装发布到npm 报错 Uncaught TypeError: Cannot read property 'toLowerCase' of undefined
Uncaught TypeError: Cannot read property 'toLowerCase' of undefined 原因是 没有导出 export default { name:& ...
- 移动端无限滚动 TScroll.vue组件
// 先看使用TScroll.vue的几个demo 1.https://sorrowx.github.io/TScroll/#/ 2. https://sorrowx.github.io/TScrol ...
- vue2.0 如何自定义组件(vue组件的封装)
一.前言 之前的博客聊过 vue2.0和react的技术选型:聊过vue的axios封装和vuex使用.今天简单聊聊 vue 组件的封装. vue 的ui框架现在是很多的,但是鉴于移动设备的复杂性,兼 ...
随机推荐
- A Telnet Client Using Expect
The following expect script achieves a simple telnet client: login -> send command -> exit. Th ...
- MyBatis学习04(注解开发)
7.使用注解开发 7.1 面向接口编程 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好 在一个面 ...
- Java工具类-输入输出流
输入输出流 1.概念 输入输出流:文件复制,上传 输出流: System.out.println() 写操作,程序将字符流写入到"目的地",比如打印机和文件等 输入流 :Scann ...
- SpringMVC/boot-CSRF安全方案
1. CSRF原理与防御方案概述 一. 原理 增删改的接口参数值都有规律可循,可以被人恶意构造增删改接口 将恶意构造的增删改接口发给对应特定用户,让特定用户点击 特定用户使用自己的认证信息对该接口发起 ...
- miniFTP项目集合
项目简介 在Linux环境下用C语言开发的Vsftpd的简化版本,拥有部分Vsftpd功能和相同的FTP协议,系统的主要架构采用多进程模型,每当有一个新的客户连接到达,主进程就会派生出一个ftp服务进 ...
- stm32 connot enter debug mode
dap 可以发现设备,stlink jlink 均无法发现设备,但是都不能下载.connot enter debug mode ,发现是vdda 未连接
- 题解 Cicada 拿衣服
传送门 神仙题! 听@Yubai给我讲了半个下午,快%@Yubai 见到这些奇奇怪怪的题是不是应该试着证下状态数上界啊 首先观察题目里给的柿子,可以发现 \(or-and\) 单调增, \(min-m ...
- 堆排序——Java实现
一.堆排序 堆排序(Heap Sort)是指利用堆这种数据结构所设计的一种排序算法.堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点. 二.堆 什 ...
- MVVMLight学习笔记(七)---Messenger使用
一.概述 Messenger中文解释为信使的意思,顾名思义,在MvvmLight中,它的主要作用是用于View和ViewModel.ViewModel和ViewModel之间的通信. 考虑以下场景: ...
- jQuery中的样式(七):addClass()、removeClass()、toggleClass()、hasClass()、css()、width()、height()等
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...