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)”,这样完美解决了.

希望对你有帮助,虽然这个组件使用中出现了好多问题,但是对于只实现滚动的需求,这个组件还是很好用的

Logo

前往低代码交流专区

更多推荐