移动端Vue实现滑动slider加hover 也就是toolti

2020-12-14  本文已影响0人  三月木头
实现滑动杠杆:

需求:
需要实现一个可以拖动,可以点击到某个百分比的滑动bar
具体效果图如下:

WeChat4ddd4c9a2ba6a56a82d0069c1150a83f.png

思路:

  1. 我们需要一个装载slider所有的容器
  2. 容器中需要有一根常驻线,也就是常见的slider灰色总长度
  3. 需要一根被选中显示不同颜色的绿线,并且这根线的width是可以动态变化的。
  4. 需要添加一个被触发滑动的视图,这个视图也就是我们通常看到的拖动的原点。
  5. 拖动原点时刻需要记录拖动的距离,这个距离也就是作为我们第3步,颜色线的长度。

实现:

  1. 首先写一个容器来装载这个slider功能
<div class="percent-slider_container">
</div>

.percent-slider_container {
    position: absolute;
    left: 10px;
    width: 192px;
}
  1. 在这个容器中,添加一跟背景图线,也就是slider长度的灰度线
<div class="percent-slider_container"> 
       <div class="percent-slider__track">   
        </div>
</div>

.percent-slider__track-slected {
    position: absolute;
    height: 4px;
    width: 50%;
    background: #24B37A;
}
  1. 在这个灰度线的基础上,添加一个被选中的绿色线,也就是拖动后显示选中的颜色(绿色).具体这个绿色多长,取决于拖动的距离
<div class="percent-slider_container"> 
       <div class="percent-slider__track">   
            <div :style="selectedStyle" class="percent-slider__track-slected"></div>
        </div>
</div>

get selectedStyle() {
       return {width:(parseFloat(this.currentPosition))+'%'}
}

.percent-slider__track-slected {
    position: absolute;
    height: 4px;
    width: 50%;
    background: #24B37A;
}
  1. 滑动条添加点击事件,长度要覆盖整个slider长度。然后需要设置5个小点平均部署在这个slider长度上。
<div class="percent-slider_container"> 
       <div class="percent-slider__track">   
            <div :style="selectedStyle" class="percent-slider__track-slected"></div>
        </div>

       <div class="percent-slider__dot-wrap" @click.self="clickTrack">
            <div class="percent-slider__dot" v-for="item in percentDots" :key="item" :style="backgroundStyle(item)" @click="selectedDot(item)"></div>
       </div>

</div>




  private percentDots:number[] = [0,25,50,75,100];

   clickTrack(event:any) {
        let value = parseFloat((event.offsetX/this.sliderSize * 100).toFixed(this.precision));
        this.$emit("input", value);
    }


.percent-slider__dot-wrap {
    top: 0;
    position: absolute;
    width: 192px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}


.percent-slider__dot {
    width: 12px;
    height: 12px;
    background: #425359;
    border-radius: 6px;
}
  1. 还差一点就是设置一个拖动点了,然后对这个拖动点添加拖动手势监听,监视拖动后记录拖动距离,然后就能计算出百分比距离了。

  private vertical = false;

<div class="percent-slider_container"> 
       <div class="percent-slider__track">   
            <div :style="selectedStyle" class="percent-slider__track-slected"></div>
        </div>

       <div class="percent-slider__dot-wrap" @click.self="clickTrack">
            <div class="percent-slider__dot" v-for="item in percentDots" :key="item" :style="backgroundStyle(item)" @click="selectedDot(item)"></div>
       </div>

       <div class="percent-slider__dot--selected"
        :style="wrapperStyle"
        @touchstart="onButtonDown">
        </div>
</div>


get wrapperStyle() {
        return this.vertical
            ? { bottom: this.currentPosition }
            : {left:(parseFloat(this.currentPosition) * this.sliderSize) / 100 + "px",};
    }

onButtonDown(event: Event) {
        event.preventDefault();
        this.onDragStart(event);
        window.addEventListener("touchmove", this.onDragging);
        window.addEventListener("touchend", this.onDragEnd);
    }

  onDragStart(event: any) {
        this.dragging = true;
        this.isClick = true;
        if (event.type === "touchstart") {
            event.clientY = event.touches[0].clientY;
            event.clientX = event.touches[0].clientX;
        }
        if (this.vertical) {
            this.startY = event.clientY;
        } else {
            this.startX = event.clientX;
        }
        this.startPosition = parseFloat(this.currentPosition);
        this.newPosition = this.startPosition;
    }


 onDragging(event: any) {
        if (this.dragging) {
            this.isClick = false;
            let diff = 0;
            if (event.type === "touchmove") {
                event.clientY = event.touches[0].clientY;
                event.clientX = event.touches[0].clientX;
            }

            if (this.vertical) {
                this.currentY = event.clientY;
                diff = ((this.startY - this.currentY) / this.sliderSize) * 100;
            } else {
                this.currentX = event.clientX;
                diff = ((this.currentX - this.startX) / this.sliderSize) * 100;
            }

            this.newPosition = this.startPosition + diff;
            this.setPosition(this.newPosition);
        }
    }

onDragEnd() {
        if (this.dragging) {
            /*
            * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移
            * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
            */
            setTimeout(() => {
                this.dragging = false;
                // this.hideTooltip();
                if (!this.isClick) {
                    this.setPosition(this.newPosition);
                //   this.$parent.emitChange();
                }
            }, 0);
            window.removeEventListener("touchmove", this.onDragging);
            window.removeEventListener("touchend", this.onDragEnd);
        }
    }

setPosition(newPosition:any) {
        if (newPosition === null || isNaN(newPosition)) return;
        if (newPosition < 0) {
            newPosition = 0;
        } else if (newPosition > 100){
            newPosition = 100;
        }

        const lengthPerStep = 100 / ((this.max - this.min) / this.step);
        const steps = Math.round(newPosition / lengthPerStep);
        let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
        value = parseFloat(value.toFixed(this.precision));
        this.$emit("input", value);

        if (!this.dragging && this.value !== this.oldValue) {
            this.oldValue = this.value;
        }
    }

.percent-slider__dot--selected {
    position: absolute;
    top: -3px;
    left: -3px;
    width: 18px;
    height: 18px;
    border-radius: 9px;
    background: #24B37A;
}

.percent-slider__dot--selected::after {
    content: '';
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 7px;
    border: 2px solid #222E33;
    top: 2px;
    left: 2px;
    box-sizing: border-box;
}

主要是注意 touch相关事件是怎么玩的:

  1. 首先给相关控件绑定 touch事件,当touch这个div的时候,我们触发onButtonDown方法,这个方法中我们首先把其它杂七杂八默认事件全给取消掉(event.preventDefault();),然后识别一下touch事件touchstart这时候是识别到准备开始拖动了,所以我们需要进行一些状态的改变( this.dragging = true;this.isClick = true;)并且记录一下开始滑动时候的起始坐标(this.startX = event.clientX;),绑定识别touchmove、touchend这些事件到对应的方法上面。用户滑动的时候,记录滑动的currentX并计算出滑动的距离,然后计算出所占slider长度的比例。滑动结束onDragEnd 的时候对相关添加绑定监听事件机型移除。

手机端 vue识别手势的时候:event.touches[0].clientY
PC端识别收拾:event.clientY

上一篇 下一篇

猜你喜欢

热点阅读