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框架现在是很多的,但是鉴于移动设备的复杂性,兼 ...
随机推荐
- FSM自动售货机 verilog 实现及 code 细节讲解
1.题目: 饮料1.5 元, 可投入硬币1 元 0.5 元,输出饮料 零钱 2. 画出状态机. 3.仿真结果:coin=1 --> 0.5 元 coin=2-->1元 4.关键代码分析: ...
- Goland 这些技巧,学会开发效率翻倍!
hi, 大家好,我是 hhf. <Goland 这些实操技巧,你可能还不会!>介绍了日常开发中一些比较好用的技巧.本篇文章继续介绍一些其他比较好用的技巧. 自定义结构 tag Goland ...
- Linux之cat tail less常见用法
1.cat 通常查找出错误日志 cat error.log | grep 'foo' , 这时候我们还有个需求就是输出当前这个日志的前后几行: cat error.log | grep -C 10 ' ...
- docker 搭建kafka集群(入门版)
1.环境 docker, docker-compose 2.zk-kafka.yml version: '3' services: zoo1: image: zookeeper:3.4.14 rest ...
- Python - typing 模块 —— Any Type
前言 typing 是在 python 3.5 才有的模块 前置学习 Python 类型提示:https://www.cnblogs.com/poloyy/p/15145380.html 常用类型提示 ...
- 【工作篇】了解升级 Spring 版本导致的跨域问题
一.背景 最近需要统一升级 Spring 的版本,避免 common 包和各个项目间的 Spring 版本冲突问题.这次升级主要是从 Spring 4.1.9.RELEASE 升级到 Spring 4 ...
- SQL 练习18
按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺 SELECT t.cid,t.sid,t.score ,COUNT(t1.score)+1 as 排名 from sc as t LE ...
- [1.1W字] 复习: CSS 9个背景属性&6种渐变函数, 学会可以手写实现AI中强大的"任意渐变"! #Archives009
Title/ CSS Background&Gradient完全指南 #Archives009 序: 关于 background 属性, 了解点CSS的人总会知道个大概. 但是你肯定多半还有点 ...
- C#录音从声卡
原文 http://stackoverflow.com/questions/18812224/c-sharp-recording-audio-from-soundcard 我想从我的声卡(输出)录 ...
- 使用VSCode创建第一个VUE项目
vue init webpack vue_test回车,然后输入工程名称vue_test vue:Missing space before value for key 'components' 原因是 ...