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;
}