美文网首页
移动端Vue实现滑动slider加hover 也就是toolti

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

作者: 三月木头 | 来源:发表于2020-12-14 11:22 被阅读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

    相关文章

      网友评论

          本文标题:移动端Vue实现滑动slider加hover 也就是toolti

          本文链接:https://www.haomeiwen.com/subject/pqsfgktx.html