vue 基于vue-seamless-scroll无缝滚动的用法和遇到的问题解决
背景
最近再做一个大屏项目,需要用到表格滚动效果,之前自己写过js实现,最近发现一个组件vue-seamless-scroll可以实现滚动,感觉挺方便的,准备用一下,但是用完之后才发现这个组件有很多坑需要解决.我把用法和一些问题的解决方法记录一下.
需求
实现表格滚动效果,表格中过长的是文字需要悬停展示,点击每行弹出详情弹框,数据每分钟更新一次.
安装vue-seamless-scroll
npm install vue-seamless-scroll --save
引入组件
<vue-seamless-scroll></vue-seamless-scroll>
import vueSeamlessScroll from 'vue-seamless-scroll'
components: {
vueSeamlessScroll
},
配置参数
// 监听属性 类似于data概念
computed: {
defaultOption () {
return {
step: 0.2, // 数值越大速度滚动越快
limitMoveNum: 2, // 开始无缝滚动的数据量 this.dataList.length
hoverStop: true, // 是否开启鼠标悬停stop
direction: 1, // 0向下 1向上 2向左 3向右
openWatch: true, // 开启数据实时监控刷新dom
singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
waitTime: 1000 // 单步运动停止的时间(默认值1000ms)
}
}
},
上边的安装使用很简单,大家可以官方文档看看.
刚开始我还觉得这个组件很好用,可是后来现实改变了我的想法.
遇到的问题
我把项目代码放上,便于下边问题描述,这是最终代码
html
<div class="table_box">
<div class="heared">
<div
:class="item.className"
v-for="(item,index) in headerList"
:key="index"
:style="'width:'+item.width+'px'"
>
{{item.title}}
</div>
</div>
<div class="scrollBox" @click="clickHang($event)">
<div ref="vc-tooltip" style="position: absolute;display: none" class="tooltip-box"></div>
<vue-seamless-scroll
@mouseover.native="handleMouseover($event)"
@mouseout.native="handleMouseout($event)"
:data="tableData"
:class-option="classOption"
class="warp"
@ScrollEnd="scrollEndEvent"
v-if="scrollFlag"
>
<div class="tableCont" ref="tableCont">
<div class="hangBox" v-for="(item,index) in tableData" :key="index" :data-obj="JSON.stringify(item)">
<div class="hang">
<div class="caseLevel">
<span class="table-content" :data-obj="JSON.stringify(item)">{{item.caseLevel}}</span>
</div>
<div class="caseType">
<span :data-obj="JSON.stringify(item)">{{getCaseType(item.caseType)}}</span>
</div>
<div class="receiveCaseTime">
<span :data-obj="JSON.stringify(item)">{{formatDate(item.receiveCaseTime)}}</span>
</div>
<div class="caseSummary">
<span class="table-content" :data-obj="JSON.stringify(item)">{{item.caseSummary}}</span>
</div>
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
</div>
js
data() {
return {
headerList: [
{ id: 1, title: '级别', className: 'caseLevel', width: 60 },
{ id: 2, title: '类型', className: 'caseType', width: 100 },
{ id: 3, title: '时间', className: 'receiveCaseTime', width: 100 },
{ id: 4, title: '概要', className: 'caseSummary', width: 130 }
],
classOption: {
direction: 1, //滚动方向
step: 0.5, //滚动速度 越打越快
singleHeight: 28, //滚动距离
limitMoveNum: 6
},
}
},
...
clickHang($event) {
if ($event.target.dataset) {
const item = JSON.parse($event.target.dataset.obj)
console.log(item )
//去打开弹框
}
},
handleMouseover(el) {
//判断el是否在content的`<td>`标签上
if (el.target.className.includes('table-content')) {
el.target.style.cursor = 'pointer'
//el所在div的transform在y轴上的移动px
let h = parseFloat(
this.$refs['tableCont'].parentElement.parentElement.style.transform
.replaceAll('translate(0px,', '')
.replace('px)', '')
)
//显示div
this.$refs['vc-tooltip'].style.display = ''
this.$refs['vc-tooltip'].innerHTML = el.target.innerHTML
this.$refs['vc-tooltip'].style.left = el.target.offsetLeft - this.$refs['vc-tooltip'].offsetWidth / 2 + 'px'
//判断el所在的div后面是否有元素,有则为第一个div
// if (el.target.parentElement.parentElement.parentElement.nextSibling != null) {
this.$refs['vc-tooltip'].style.top = el.target.offsetTop + h - 10 + 'px'
// } else {
// //当el所在div为第二个即最后一个,弹窗的top还需加上上一个div的高度
// this.$refs['vc-tooltip'].style.top =
// el.target.offsetTop + h + this.$refs['tableCont'].offsetHeight - 10 + 'px'
// }
}
},
handleMouseout(el) {
if (el.target.className.includes('table-content')) {
this.$refs['vc-tooltip'].style.display = 'none'
}
},
css
.table_box {
margin: 5px;
text-align: center;
position: relative;
.heared {
display: flex;
height: 21px;
line-height: 21px;
align-items: center;
justify-content: space-between;
background-color: rgba(6, 90, 254, 0.8);
& > div {
margin-left: 5px;
font-family: Noto Sans SC;
font-size: 16px;
font-weight: 700;
color: #fccd1d;
}
}
.scrollBox {
.tooltip-box {
z-index: 5;
background: #303133;
color: #fff;
padding: 10px;
line-height: 1.2;
min-width: 10px;
word-wrap: break-word;
border-radius: 4px;
font-size: 12px;
}
}
.warp {
height: 146px;
overflow: hidden;
// margin: 10px 0;
}
.tableCont {
.hangBox {
height: 27px;
pointer-events: all;
padding-top: 1px;
}
.hang {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 9px;
height: 18px;
line-height: 18px;
font-style: normal;
font-size: 16px;
color: #ffffff;
background-color: rgba(6, 90, 254, 0.5);
font-family: Noto Sans SC;
pointer-events: none;
& > div {
margin-left: 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.table-content {
pointer-events: all;
}
}
.caseLevel {
width: 60px;
}
.receiveCaseTime {
width: 100px;
}
.caseType {
width: 100px;
}
.caseSummary {
width: 130px;
}
}
}
}
1.每一行挂载点击事件
开始的思路是在表格行上添加@click事件就行了,这种方式不可行,发现有的行点击事件挂载不上,后来看了看,明白了大概原因,
由于插件为了实现无缝滚动的效果会把表格html部分复制一份,用于滚动的衔接,但是复制的部分不会挂载点击事件.
解决方法
思路:在每一行中添加属性值, :data-obj=“JSON.stringify(item)”,再通过外层的点击事件clickHang,获取目标元素的属性值.
这里还会引发一个问题的是:
如果每一行中的元素结构比较复杂,:data-obj="JSON.stringify(item)"挂载的位置需要考虑一下,如果挂错地方了,外层点击事件获取不到属性值.
这里我使用css属性把hangBox中hang属性pointer-events: none;然后把hangBox元素设置pointer-events: all;目的就是只留下hangbox的触发事件,内部元素就不会影响了.
2.表格中每一行过长,悬停提示
开始思路是直接用elementui中组件el-tooltip实现,但是又遇到问题了,还是上边的问题,插件把表格html部分复制的部分不会有提示.
解决方法
思路:在外边写个弹框,在组件上挂上,mouseover,mouseout事件,通过鼠标事件控制弹框的显隐,代码在上边大家可以去看看.
这里也遇到了一个问题
由于上边为了解决行点击事件,准确获取每行数据问题,咱们把.hang元素的属性pointer-events: none;导致hang中的元素触发不了悬停,所以得把里边的table-content元素属性设置成pointer-events: all;在这个元素上挂上属性:data-obj=“JSON.stringify(item)”,这样完美解决了.
希望对你有帮助,虽然这个组件使用中出现了好多问题,但是对于只实现滚动的需求,这个组件还是很好用的