vue-seamless-scroll插件,使用详细( vue-seamless-scroll 无缝滚动)

前言

vue-seamless-scroll是一个基于Vue.js的无缝滚动插件,支持上下左右无缝滚动、单步滚动,以及支持水平方向的手动切换功能,配置多,满足日常开发需求,基于requestAnimationFrame实现,下面给大家介绍一下,具体的使用详细

一. 安装相关依赖
//vue2
  import scroll from 'vue-seamless-scroll'
  Vue.use(scroll)
 
//vue3  代码多去少补
  import { createApp } from 'vue';
  import App from './App.vue';
  import vue3SeamlessScroll from "vue3-seamless-scroll";
  const app = createApp(App);
  app.use(vue3SeamlessScroll);
  app.mount('#app');
二. 全局引入组件或者使用局部引入
//vue2
  import scroll from 'vue-seamless-scroll'
  Vue.use(scroll)
 
//vue3  代码多去少补
  import { createApp } from 'vue';
  import App from './App.vue';
  import vue3SeamlessScroll from "vue3-seamless-scroll";
  const app = createApp(App);
  app.use(vue3SeamlessScroll);
  app.mount('#app');
//组价使用三步骤1、引入 2、注册 3、使用
import vueSeamlessScroll from 'vue-seamless-scroll'      //1、引入
components: {                                            //2、注册
        vueSeamlessScroll
}, 
<vue-seamless-scroll></vue-seamless-scroll>              //3、使用
 
 
//vue3 同上 代码多去少补
import { defineComponent } from "vue";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
export default defineComponent({
      components: {
        Vue3SeamlessScroll
      }
   })
<vue3-seamless-scroll></vue3-seamless-scroll>
三. 组件使用,配置相关属性
<template>
    <!--
          根目录规范(必须不能为空):
          idm-ctrl:控件类型 drag_container:容器,drag_container_inlieblock:行内容器,idm_module:非容器的组件
          id:使用moduleObject.id,如果id不使用这个将会被idm-ctrl-id属性替换
          idm-ctrl-id:组件的id,这个必须不能为空
        -->
    <div idm-ctrl="idm_module" :id="moduleObject.id" :idm-ctrl-id="moduleObject.id" class="i-RollingList-outer"
        ref="module_ref" :style="propData.heightType == 'adaptive' ? { height: moduleHeight + 'px' } : {}
            ">
        <div class="i-RollingList-context">
            <div class="headBox">
                <div class="left">
                    季考核等次为“好”的人员
                </div>
                <!-- <div class="right" @click="onMoreUrl('')">
                    <el-tooltip class="item" effect="dark" content="更多" placement="bottom">
                        <i class="el-icon-more"></i>
                    </el-tooltip>
                </div> -->
            </div>
            <div class="main" ref="parentElement">
                <div  @click="showInfo" class="content">
                    <vue-seamless-scroll v-if="dataList.length" :key="timeKey" :data="dataList" :class-option="scrollOption" class="info">
                        <div class="listBox">
                            <div v-for="(item,index) in dataList" :key="index" :data-index="index" >
                                <img :src="IDM.url.getModuleAssetsWebPath(require(`@/assets/icon1.png`))" alt=""> {{item}}
                            </div>
                        </div>
                    </vue-seamless-scroll>
                    <div v-else class="wsj">暂无数据</div>
                </div>
            </div>
        </div>
    </div>
</template>
    
<script>

import vueSeamlessScroll from 'vue-seamless-scroll'   
//这个是放本地开发测试数据
const devResult = () => {
    let data = {
        "data": [
            [
                "[秘书处]:王老师,机老师",
                "[干部人事处]:杜老师,彭老师,汪老师",
                "[动员协调处]:王老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
                "[秘书处]:王老师,机老师",
                "[干部人事处]:杜老师,彭老师,汪老师",
                "[动员协调处]:王老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
                "[秘书处]:王老师,机老师",
                "[干部人事处]:杜老师,彭老师,汪老师",
                "[动员协调处]:王老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
                "[政策法规处(审计监督室)]:李老师,陈老师",
               
            ]
        ],
        "url": "http://localhost:8080/DreamWeb/ctrl/website/api/contentJump.html?contentId=240907161601EvCa9hieQyINrlIWQLY"
    }
    return data
}
export default {
    name: 'WorkLedger',
    components: {                                            //2、注册
            vueSeamlessScroll
    },
    data() {
        return {
            moduleObject: this._moduleObject || {},
            moduleHeight: 0,
            propData: this._propData?.compositeAttr || this.$root?.propData?.compositeAttr || {
                heightType: "adaptive",
                JumpType:"_block",
                isJK: true,
                customInterfaceUrl: "ctrl/p2531indexpc/indexDbData",
            },
            limitMoveNum:0,//滚动区域的高度
            url:"",
            dataList: []
    },
    computed: {
        scrollOption(){
            return  {
                step: 0.5, // 数值越大速度滚动越快
                //这里动态获取的滚动的数据量,超出高度的时候滚动
                limitMoveNum: Math.floor( (this.moduleHeight-66) / 30)+1, // 开始无缝滚动的数据量 this.list.length+1
                hoverStop: true, // 是否开启鼠标悬停stop
                direction: 1, // 0向下 1向上 2向左 3向右
                openWatch: true, // 开启数据实时监控刷新dom
                singleHeight: 30, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
                singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
                waitTime: 0, // 单步运动停止的时间(默认值1000ms)
            }
        }
    },
    props: {
        _moduleObject: Object,
        _propData: Object
    },
    created() {
        if (process.env.NODE_ENV == "development") {
            // this.login()
        }
        this.moduleHeight = this.propData.moduleHeight
        this.moduleObject = this._moduleObject || this.$root.moduleObject;
        this.resizeContentWrapperHeight(300);
    },
    mounted() {
        //直接使用组件此处的回调必须的
        this._moduleObject && IDM.callBackComponentMountComplete?.apply(this, [this._moduleObject]);
        this.initData(true)
    },
    methods: {
        //初始化数据
        initData(flag) {
            const param = this.commonParam();
            if (!this.propData.dataSource || !this.propData.dataSource[0]?.id) {
                if (this.moduleObject.env !== 'production') {
                    //开发时的数据源
                    this.dataList = devResult().data[0]
                    this.url=devResult().url
                    console.log(this.dataList, "==++++");
                    this.$nextTick().then(() => {
                        const parentHeight = this.$refs['parentElement'].offsetHeight || 0;
                        let limitMoveNum=Math.floor(parentHeight / 30)+1
                        console.log('父元素的高度:', parentHeight,limitMoveNum);
                    });
                    return;
                }
            }
            console.log(this.propData.isJK, "是否使用接口");
            if (this.propData.isJK == false) {
                console.log('-----++++数据源数据');
                IDM.datasource.request(this.propData.dataSource[0]?.id, {
                    moduleObject: this.moduleObject,
                    param
                }, (data) => {
                    console.log(data, "+++++");
                    this.dataList = data.data[0] || {};
                    this.url=data.url
                })
            } else {
                console.log('-----++++接口数据');
                let url = this.propData.customInterfaceUrl //this.propData.customInterfaceUrl
                let params = {
                }
                window.IDM.http.get(url, params).then((res) => {
                    console.log(res, res.data.data.url,"+++++");
                    this.dataList = res.data.data.data[0] || {};
                    this.url=res.data.data.url
                }).catch(function (error) {
                });
            }
            this.$nextTick().then(() => {
                const parentHeight = this.$refs['parentElement'].offsetHeight || 0;
                let limitMoveNum=Math.floor(parentHeight / 30)+1
                console.log('父元素的高度:', parentHeight,limitMoveNum);
            });
        },
        //在滚动区域的通过main来获取组件vue-seamless-scroll组件中每一行的点击事件
        showInfo(e) {
            console.log(e.target.dataset.index);
            let idx = e.target.dataset.index;
            console.log(this.dataList[idx]) //点击行数据
            // e:鼠标事件,e.target:点击元素(<td>),e.target.parentNode:点击元素父元素(<tr>),e.target.parentNode.dataset:自定义属性位置
                this.onItemClick({url:this.url})
        },
        //点击事件
        onItemClick(config) {
            console.log(config);
            console.log("_______点击事件", this.propData.JumpType, config);
            if (this.moduleObject.env !== 'production') {
                console.log(456789);
                return;
            }
            //自定义函数
            if (this.propData.JumpType === 'custom') {
                console.log(this.propData.JumpType);
                this.customFunctionHandle(this.propData.customMoreBtnFunction, config);
                return
            } else if (this.JumpType === 'main') { //内嵌标签页打开
                const item = {
                    isTabReload: "-1",
                    name: config.moduleName,
                    action: IDM.url.getWebPath(config.url),
                    target: "main",
                    parentName: ""
                };
                // window.$$iframeCtrl && window.$$iframeCtrl.addTab(item);
                top.window.$$iframeCtrl.addTab(item)
            } else if (config.url && config.url.length > 0) {
                const url = IDM.url.getWebPath(config.url);
                window.open(url, this.JumpType || '_block');
            }
        },
        /**
         * 提供父级组件调用的刷新prop数据
         */
        propDataWatchHandle(propData) {
            this.propData = propData.compositeAttr || {};
            this.resizeContentWrapperHeight();
            this.initData(true)
        },
        resizeContentWrapperHeight(wrapperHeight, wrapperWidth) {
            wrapperHeight = wrapperHeight || $('#' + this.moduleObject.packageid)
                .parents('.fsl-region-element')
                .height();
            wrapperWidth = wrapperWidth || $('#' + this.moduleObject.packageid)
                .parents('.fsl-region-element')
                .width();
            let moduleTBMarginNumber = 0;
            const iAttrArray = ['marginTopVal', 'marginBottomVal'];
            iAttrArray.forEach(item => {
                if (this.propData.box && this.propData.box[item]) {
                    if (this.propData.box[item].indexOf('%') > -1) {
                        //用宽度计算出实际的px
                        moduleTBMarginNumber =
                            moduleTBMarginNumber +
                            (parseFloat(this.propData.box[item].replace('%', '')) / 100) * wrapperWidth;
                    } else {
                        moduleTBMarginNumber =
                            moduleTBMarginNumber + parseFloat(this.propData.box[item].replace('px', ''));
                    }
                }
            });
            this.moduleHeight = wrapperHeight - moduleTBMarginNumber;
        },
        /**0
         * 组件通信:接收消息的方法
         */
        receiveBroadcastMessage(messageObject) {
            switch (messageObject.type) {
                case 'regionResize':
                    if (messageObject.message && messageObject.message.gridEleTarget) {
                        const {
                            offsetHeight,
                            offsetWidth
                        } = messageObject.message.gridEleTarget
                        this.resizeContentWrapperHeight(offsetHeight, offsetWidth);
                        this.initData(true)
                    }
                    break;
                case 'linkageResult':
                    if (messageObject.message) {
                        let data = this.propData.dataFiled ? this.getExpressData('data', this.propData.dataFiled, messageObject.message) : messageObject.message;
                        this.data = this.customFormat(this.propData.dataCustomFunction, data);
                    }
                    break;
            }
        },
        /**
       * 通用的获取表达式匹配后的结果
       */
        getExpressData(dataName, dataFiled, resultData) {
            //给defaultValue设置dataFiled的值
            if (!dataName && dataName.length <= 0 && !resultData && resultData.length <= 0) {
                return
            }
            let _defaultVal = undefined;
            if (dataFiled) {
                let filedExp = dataFiled;
                filedExp = dataName + (filedExp.startsWiths('[') ? '' : '.') + filedExp;
                let dataObject = { IDM: window.IDM };
                dataObject[dataName] = resultData;
                _defaultVal = window.IDM.express.replace.call(this, '@[' + filedExp + ']', dataObject, false);
            }
            return _defaultVal;
        },
        customFormat(customFunction, data) {
            if (customFunction && customFunction[0] && customFunction[0].name) {
                data =
                    window[customFunction[0].name] &&
                    window[customFunction[0].name].call(this, {
                        commonParam: this.commonParam(),
                        customParam: customFunction[0].param,
                        data
                    });
            }
            return data;
        },
        customFunctionHandle(customFunction, param = {}) {
            let urlObject = window.IDM.url.queryObject();
            let pageId = window.IDM.broadcast && window.IDM.broadcast.pageModule ? window.IDM.broadcast.pageModule.id : "";
            var clickFunction = customFunction;
            clickFunction && clickFunction.forEach((item1) => {
                window[item1.name] &&
                    window[item1.name].call(this, {
                        urlData: urlObject,
                        pageId,
                        commonParam: this.commonParam(),
                        customParam: item1.param,
                        ...param,
                        this: this,
                    });
            });
        },
        commonParam() {
            return {
                moduleObject: this.moduleObject,
                pageId: window.IDM.broadcast && window.IDM.broadcast.pageModule ?
                    window.IDM.broadcast.pageModule.id : '',
                urlObject: IDM.url.queryObject()
            };
        },
    }
};
</script>
<style lang="scss">
.i-RollingList-outer {
    width: auto;
    height: auto;
    box-sizing: border-box; 
     margin: 15px 0 10px 10px;
    margin-right: 3px;

    .i-RollingList-context {
        height: 100%;
        width: 100%;
        font-family: PingFangSC-Semibold;
        font-size: 20px;
        color: #FFFFFF;
        .headBox {
            height: 51px;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
            .left {
                position: relative;
                height: 100%;
                display: flex;
                align-items: center;
                font-weight: 600;
            }

            .right {
                display: flex;
                align-items: center;
                cursor: pointer;
                margin-right: 5px;

                .el-icon-more {
                    font-size: 18px;
                    color: #CCD3E3;
                }
            }

            .right:hover {
                .el-icon-more {
                    color: #FFFFFF;
                }
            }
        }
        .main{
            width: 100%;
            height: calc(100% - 66px);
            background: #F2F8FF;
            border-radius: 10px;
            .content{
                font-family: PingFangSC-Regular;
                font-size: 16px;
                color: #333333;
                line-height: 24px;
                font-weight: 400;
                height: 100%;
                overflow: hidden;
                .listBox{
                    &>div{
                        height: 30px;
                        display: flex;
                        align-items: center;
                        padding: 0 24px;
                        cursor: pointer;
                        img{
                            width: 13px;
                            height: 13px;
                            margin-right: 8px;
                        }
                    }
                }
            }
            .wsj{
                font-size: 16px;
                color:#999;
                text-align: center;
                padding-top: 50px;
            }
        }
    }
}</style>
//vue3 eg:
<vue3-seamless-scroll
    hover-stop="true"
    :list="listData"
    hover="true"
    step="0.3">
    <div
      v-for="(item, index) in listData"
      :key="index"
      class="item"
      style="padding: 10px; margin: 5px; font-size: 14px"
    >
      <span 
        v-for="(value, key, ind) in item" 
        :key="ind" 
        class="spanClass">
        {{value}}
      </span>
    </div>
</vue3-seamless-scroll>
四、注意事项
  1. vue-seamless-scroll
    滚动数据data必须设置,否则将不会滚动。样式需要设置高度与overflow: hidden;
  2. 单步运动停止的高度
    单步运动停止的高度应该为单行数据高度的整数倍,在文章示例中就是<tr>高度的整数倍。当设置为整数倍时,列表文字都可以清楚展示看做为无缝滚动,否则就会出现文字展示不全的问题。
  3. 单行数据添加点击事件无效
    一般情况下我们会把点击事件添加到循环行<tr>上,但是由于此组件的原因,点击事件会出现问题。解决方法,给父元素绑定事件,在子元素上进行事件捕获。
  • 父元素绑定事件:父容器添加click事件showInfo,如果需要传入其他参数,showInfo可以改为showInfo($event, param),param为传入参数
  • 在循环行上添加自定义的 data-x 属性
  • 在事件showInfo中通过鼠标事件e与自定义属性进行查找
<div class="title-text">
    <table border="0" cellpadding="0" cellspacing="0">
      <tr>
        <th>任务名称</th>
        <th>执行人</th>
        <th>当前进度</th>
      </tr>
    </table>
</div>
<div v-if="list.length" @click="showInfo" class="content">
    <vue-seamless-scroll :key="timeKey" :data="list" :class-option="scrollOption" class="info">
      <table border="0" cellpadding="0" cellspacing="0">
        <tr v-for="(item, index) in list" :key="index" :data-index="index">
          <td>{{item.title}}</td>
          <td>{{item.name}}</td>
          <td>{{item.progress}}</td>
        </tr>
      </table>
    </vue-seamless-scroll>
</div>
<div v-else class="empty">暂无数据</div>
computed: {
  scrollOption () {
    return {
      step: 0.5, // 数值越大速度滚动越快
      limitMoveNum: 6, // 开始无缝滚动的数据量 this.list.length+1
      hoverStop: true, // 是否开启鼠标悬停stop
      direction: 1, // 0向下 1向上 2向左 3向右
      openWatch: true, // 开启数据实时监控刷新dom
      singleHeight: 21, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
      singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
      waitTime: 2500 // 单步运动停止的时间(默认值1000ms)
    }
  }
},

页面存在定时器,数据实时刷新时,数据量(list.length)减少,由可滚动数据量变为不可滚动,页面显示依然滚动,limitMoveNum未生效。
在vue-seamless-scroll增加key属性,避免组件复用,每次数据请求时刷新key。key可以定义为当前时间(new Date().getTime())。

showInfo(e) {
  let idx = e.target.parentNode.dataset.index;
  console.log(this.list[idx]) //点击行数据
  // e:鼠标事件,e.target:点击元素(<td>),e.target.parentNode:点击元素父元素(<tr>),e.target.parentNode.dataset:自定义属性位置
}
五、参考链接

vue-seamless-scroll官方文档
vue-seamless-scroll插件在线演示文档
vue-seamless-scroll的使用以及实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值