Vue 一键巡游组件,节点自动/手动切换,面板可控制水平滚动

功能

  • 节点样式动态变化,模拟未进行、当前、已进行三种效果
  • 自动播放,也可以手动点击节点切换
  • 根据节点数量改变布局,节点较少时均匀分布,节点较多时固定节点间距离,通过控制左右箭头实现横向滚动,滚动到最右(左)时隐藏右(左)箭头

效果

节点数少于规定值(我这里定<=10),隐藏左右箭头,节点均匀分布

节点数大于规定值,出现左右箭头,固定节点间距离,可以横向滚动

重点

  • 伪类样式及修改
  • 监听滚动事件

完整代码

html

    <div v-show="imgChange" class="bar">
        <!-- 左箭头 -->
        <div :class="['toLeft', menuLeft ? '' : 'hide']" @click="toLeft">
            <img src="../../static/img/to_left.png" />
        </div>
        <!-- 要控制的面板 -->
        <div class="box">
            <ul class="ul" ref="menu" @scroll="orderScroll">
                <div class="ul-content">
                <li v-for="(point,i) in list" :key="i" :class="['point', (activeIndex == undefined || i > activeIndex) ? '': i == activeIndex ? 'active' : 'over']">
                    <div class="curPoint">{{point.name}}</div>
                    <div class="marker-out" @click="pointChange(point, i)">
                        <div class="marker-in"></div>
                    </div>
                    <div class="label" @click="pointChange(point, i)">
                        <el-tooltip class="item" effect="dark" :content="point.label" placement="bottom">
                            <span>{{point.label}}</span>
                        </el-tooltip>
                    </div>
                </li>
                </div>
            </ul>
        </div>
        <!-- 右箭头 -->
        <div :class="['toRight', menuRight ? '' : 'hide']" @click="toRight">
            <img src="../../static/img/dataCenter/to_right.png" />
        </div>
    </div>

data

        list: [
            {
                label: '标签1',
                name: '名称1'
            }
        ],
        activeIndex: undefined,
        menuLeft: false,
        menuRight: true,
        maxScrollLeft: undefined,
        interval: undefined

mounted

        this.init()
        document.addEventListener('scroll',this.Scroll())

methods

        init(){
            let that = this
            if(this.list.length <= 10){ //根据节点数量改变布局方式
                document.getElementsByClassName('ul')[0].classList.add("flex")
                document.getElementsByClassName('toLeft')[0].classList.add("hide")
                document.getElementsByClassName('toRight')[0].classList.add("hide")
            }
            this.$nextTick(() => {
                document.getElementsByClassName('marker-out')[0].classList.add("first")
            })
        },
        autoProcess(){
            let that = this;
            this.activeIndex = undefined
            let length = this.list.length
            this.interval = setInterval(() => {
                if(that.activeIndex == undefined){
                    that.activeIndex = 0
                    that.makePopup(that.list[that.activeIndex]) //切换节点同时切换弹窗
                }else if(that.activeIndex < length-1){
                    that.activeIndex++
                    that.makePopup(that.list[that.activeIndex])
                }else{
                    clearInterval(that.interval)
                }
            }, 1000); 
        },
        layoutAdaptation(){
            let that = this
            this.$nextTick(() => {
                let elWidth = document.getElementsByClassName('point')[0].scrollWidth
                let lineWidth = elWidth - 22
                let lineLeft = 0 - lineWidth
                for(let i=0;i<document.styleSheets.length;i++){
                    document.styleSheets[i].addRule('.flex .marker-out:before', `width:${lineWidth}px;left:${lineLeft}px`)
                } //这里不能用foreach
                that.calMaxScrollLeft() //计算最大横向滚动像素值
            })
        },
        pointChange(point, i){
            this.activeIndex = i
            this.makePopup(point)
        },
        handlePointChange(index){
            this.pointChange(this.list[index], index)
        },
        makePopup(point){ //切换弹窗
            this.$emit('switchPopup', point)
        },
        calMaxScrollLeft(){
            let menuUl = document.getElementsByClassName('ul')[0]
            this.maxScrollLeft = menuUl.scrollWidth - menuUl.offsetWidth
        },
        toLeft(){
            this.$refs.menu.scrollLeft = this.$refs.menu.scrollLeft - 50 //滚动步长自行设置
        },
        toRight(){
            this.$refs.menu.scrollLeft = this.$refs.menu.scrollLeft + 50 
        },
        Scroll(e){
            // console.log('scrollLeft',this.$refs.menu.scrollLeft)
        },
        orderScroll(){
            // console.log('this.$refs.menu.scrollLeft',this.$refs.menu.scrollLeft)
            if(this.$refs.menu.scrollLeft > 0){
                this.menuLeft = true
            }else{
                this.menuLeft = false
            }
            if(this.$refs.menu.scrollLeft >= (this.maxScrollLeft - 1)){
                this.menuRight = false
            }else{
                this.menuRight = true
            }
        }   

watch

      flag: function(newVal,oldVal) {
          if(this.flag){
            this.autoProcess()
            this.layoutAdaptation()
          }else{
              clearInterval(this.interval)
          }
      }

css

.bar{
    height: 143px;
    width: 1162px;
    background: url('../../static/img/tourBar.png') 100% 100% no-repeat;
    display: flex;
}

/* 左右箭头 */
.toLeft, .toRight{
    width: 70px;
    line-height: 143px;
}

.toLeft img, .toRight img{
    cursor: pointer;
}

.toLeft{
    text-align: right;
    padding-right: 10px;
}

.toRight{
    text-align: left;
    padding-left: 10px;
}

/* 要控制的面板 */
.box{
    width: 1022px;
}

.ul{
    display: -webkit-box;
    color: white;   
    padding: 30px 0 0; 
    overflow: auto;
}

.ul-content{
    display: flex;
}

.point{
    width: 103px;
}

.curPoint{
    font-size: 12px;
    color: #FF9702FF;
    font-weight: bold;
    visibility: hidden;
}

.marker-out{
    width: 22px;
    height: 22px;
    background: #1358B3;
    border-radius: 50%;
    padding: 4px;
    margin: 0 auto;
    position: relative;
    cursor: pointer;
}

.marker-in{
    width: 14px;
    height: 14px;
    background: #35FFF5;
    box-shadow: 2px 1px 8px 0px rgba(9, 53, 117, 0.71);
    border-radius: 50%;
}

/* 节点左边的线段 */
.marker-out:before{
    height: 0;
    width: 81px;
    border-top: 3px dashed #0B4A8A;
    position: absolute;
    left: -81px;
    top: 50%;
    content: "";
    pointer-events: none;
}

.label{
    margin: 0 auto;
    margin-top: 14px;
    width: 86px;
    height: 26px;
    line-height: 26px;
    font-size: 12px;
    background: url('../../static/img/pane.png') no-repeat;
    background-size:100% 100%; 
    opacity: 0.9;   
    position: relative; 
    cursor: pointer;
    overflow: hidden;
    text-overflow:ellipsis; 
    white-space: nowrap;
    padding: 0 5px;
}

/* 原本是用伪类做标签左上角和右下角的形状的,但是左上角的梯形实现起来有点麻烦,最后用了切图(感谢美工大哥) */
/* .label:before{
    width: 3px;
    height: 6px;
    background: #00A3FF;
    position: absolute;
    left: -1px;
    top: -1px;
    content: "";
}
.label:after{
    width: 0;
    height: 0;
    border: solid transparent;
    border-width: 3px 4px;
    border-right-color: #00A3FF;
    border-bottom-color: #00A3FF;
    position: absolute;
    right: -1px;
    bottom: -1px;
    content: "";
} */

/* 当前活动状态的节点,圆圈替换成摄像头gif */
.active .marker-out {
    background: url('../../static/img/icon_video.gif') no-repeat;
    background-position: center; 
}
.active .marker-in {
    display: none;
}
/* 当前节点显示下箭头指向标签 */
.active .marker-out:after{
    width: 0;
    height: 0;
    border: solid transparent;
    border-width: 7px 6px;
    border-top-color: #FF9702; 
    position: absolute;
    bottom: -19px;
    left: 5px;
    content: "";
}
.active .label{
    color: #FF9702FF;
    background: url('../../static/img/pane_active.png') no-repeat;
    background-size:100% 100%;     
}
/* 当前节点上方显示名称 */
.active .curPoint{
    visibility: visible;
}
/* 当前节点改变左边线段样式 */
.active .marker-out:before, .over .marker-out:before{
    border-top: 2px solid #369EFF;
    border-bottom: 2px solid #369EFF;
}

/* 已经过的节点 */
.over .marker-in{
    background: #359EFF;
    box-shadow: 2px 1px 8px 0px rgba(9, 53, 117, 0.71);   
}

/* 节点少时均匀分布 */
.flex{
    display: flex;
    flex-direction: row;
}
.flex .point{
    flex: 1;
}
.flex .ul-content{
    width: 100%;
}

/* 节点少时或滚动到最左/最右侧时隐藏箭头 */
.hide{
    visibility: hidden;
    pointer-events: none;
}
.ul::-webkit-scrollbar {
    height: 0;     
}

/* 隐藏第一个节点左侧的线段 */
.first:before{
    display: none;
}

参考

posted @ 2020-10-04 19:54  宇宙野牛  阅读(431)  评论(0编辑  收藏  举报