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框架现在是很多的,但是鉴于移动设备的复杂性,兼 ...
随机推荐
- bat脚本中%~dp0含义解释
在Windows脚本中,%i类似于shell脚本中的$i,%0表示脚本本身,%1表示脚本的第一个参数,以此类推到%9,在%和i之间可以有"修饰符"(完整列表可通过"for ...
- awk-07-IO和printf语句
IO语句 1.getline 2.getline var 把a文件的行,追加到b文件的结尾 把 a 文件的行替换 b 文件的指定字段 把 a 文件的行替换 b 文件的对应字段 3.command | ...
- linux 磁盘IO速度测试
写入速度测试命令:time dd oflag=direct if=/dev/zero of=/data2/test bs=2k count=1000000 //if表示从哪里读取 of表示写入到哪里 ...
- Pikachu-php反序列化、XXE、SSRF模块
一.PHP反序列化 理解这个漏洞需要明白php中的两个函数,serialize(),unserialize() 1)序列化serialize()序列化说通俗点就是把一个对象变成可以传输的字符串,比如下 ...
- deepin设置jdk全局变量
sudo vim /etc/bash.bashrc 在文件最后边添加 JAVA_HOME=jdk地址CLASSPATH=.:$JAVA_HOME/bin.tools.jarPATH=$JAVA_HOM ...
- qt 中的自定义pushbutton
- LeetCoded第206题题解--反转链表
反转一个单链表. 示例 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 方法一:递归 自始至 ...
- react启动报babel-eslint依赖版本不一致
遇到的问题: gitlab上拉的react的项目,下载依赖之后,yarn start启动报错 因为按他说的步骤:1.改动比较大, 2.完全没什么作用(重点) 解决方案:直接忽略,如果项目没有.env文 ...
- java js转码
public static String escape(String src) { int i; char j; StringBuffer tmp = new StringBuffer(); tmp. ...
- 存储系统管理(一)——Linux系统的设备和分区管理
1.设备名称的理解 /dev/sda1? sata硬盘,a1表示第一块硬盘中的第一个分区 /dev/cdrom 光驱 /dev/mapper/*? 系统中的虚拟设备 2.发现系统中的设备 ? fdis ...