| /** | 
|  * 使用bindingx方案实现slider | 
|  * 只能使用于nvue下 | 
|  */ | 
| // 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损 | 
| const BindingX = uni.requireNativePlugin('bindingx') | 
| // nvue操作dom的库,用于获取dom的尺寸信息 | 
| const dom = uni.requireNativePlugin('dom') | 
| // nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue | 
| const animation = uni.requireNativePlugin('animation') | 
|   | 
| export default { | 
|     data() { | 
|         return { | 
|             // bindingx的回调值,用于取消绑定 | 
|             panEvent: null, | 
|             // 标记是否移动状态 | 
|             moving: false, | 
|             // 位移的偏移量 | 
|             x: 0, | 
|             // 是否正在触摸过程中,用于标记动画类是否添加或移除 | 
|             touching: false, | 
|             changeFromInside: false | 
|         } | 
|     }, | 
|     watch: { | 
|         // 监听vlaue的变化,此变化可能是由于内部修改v-model的值,或者外部 | 
|         // 从服务端获取一个值后,赋值给slider的v-model而导致的 | 
|         value(n) { | 
|             if (!this.changeFromInside) { | 
|                 this.initX() | 
|             } else { | 
|                 this.changeFromInside = false | 
|             } | 
|         } | 
|     }, | 
|     mounted() { | 
|         this.init() | 
|     }, | 
|     methods: { | 
|         init() { | 
|             this.getSliderRect() | 
|         }, | 
|         // 获取节点信息 | 
|         // 获取slider尺寸 | 
|         getSliderRect() { | 
|             // 获取滑块条的尺寸信息 | 
|             // 通过nvue的dom模块,查询节点信息 | 
|             setTimeout(() => { | 
|                 dom.getComponentRect(this.$refs['slider'], res => { | 
|                     this.sliderRect = res.size | 
|                     this.initX() | 
|                 }) | 
|             }, 10) | 
|         }, | 
|         // 初始化按钮位置 | 
|         initButtonStyle({ | 
|             barStyle, | 
|             buttonWrapperStyle | 
|         }) { | 
|             this.barStyle = barStyle | 
|             this.buttonWrapperStyle = buttonWrapperStyle | 
|         }, | 
|         emitEvent(event, value) { | 
|             this.$emit(event, value ? value : this.value) | 
|         }, | 
|         formatStep(value) { | 
|             // 移动点占总长度的百分比 | 
|             return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step | 
|         }, | 
|         // 滑动开始 | 
|         onTouchStart(e) { | 
|             // 阻止页面滚动,可以保证在滑动过程中,不让页面可以上下滚动,造成不好的体验 | 
|             e.stopPropagation && e.stopPropagation() | 
|             e.preventDefault && e.preventDefault() | 
|             if (this.moving || this.disabled) { | 
|                 // 释放上一次的资源 | 
|                 if (this.panEvent?.token != 0) { | 
|                     BindingX.unbind({ | 
|                         token: this.panEvent.token, | 
|                         // pan为手势事件 | 
|                         eventType: 'pan' | 
|                     }) | 
|                     this.gesToken = 0 | 
|                 } | 
|                 return | 
|             } | 
|   | 
|             this.moving = true | 
|             this.touching = true | 
|   | 
|             // 获取元素ref | 
|             const button = this.$refs['nvue-button'].ref | 
|             const gap = this.$refs['nvue-gap'].ref | 
|   | 
|             const { | 
|                 min, | 
|                 max, | 
|                 step | 
|             } = this | 
|             const { | 
|                 left, | 
|                 width | 
|             } = this.sliderRect | 
|   | 
|             // 初始值为本次偏移量x,加上次停止滑动时的结束值 | 
|             let exporession = `(${this.x} + x)` | 
|             // 将偏移的x值,转为总位移的百分比值,为了和min和max进行判断 | 
|             exporession = `(${exporession} / ${width}) * 100` | 
|             if (step > 1) { | 
|                 // 如果step步进大于1,需要跳步,所以需要使用Math.round进行取整 | 
|                 exporession = `round(max(${min}, min(${exporession}, ${max})) / ${step}) * ${step}` | 
|             } else { | 
|                 // 当step=1时,无需跳步,充分利用bindingx性能,滑块实时跟随手势,达到丝滑的效果 | 
|                 exporession = `max(${min}, min(${exporession}, ${max}))` | 
|             } | 
|             // 将百分比最后转化为对应的px值 | 
|             exporession = `${exporession} / 100 * ${width}` | 
|             // 最大值不允许超过轨迹的宽度 | 
|             const { | 
|                 sliderWidth | 
|             } = this.sliderRect | 
|             exporession = `min(${sliderWidth}, ${exporession})` | 
|             // 滑块点总是需要一个左偏移的值,为自身宽度的一半 | 
|             const buttonExpression = `${exporession} - ${this.blockHeight / 2}` | 
|             // 阿里为了KPI而开源的BindingX | 
|             this.panEvent = BindingX.bind({ | 
|                 anchor: button, | 
|                 eventType: 'pan', | 
|                 props: [{ | 
|                     element: gap, | 
|                     // 绑定width属性,设置其宽度值 | 
|                     property: 'width', | 
|                     expression | 
|                 }, { | 
|                     element: button, | 
|                     // 绑定width属性,设置其宽度值 | 
|                     property: 'transform.translateX', | 
|                     expression: buttonExpression | 
|                 }] | 
|             }, (e) => { | 
|                 if (e.state === 'end' || e.state === 'exit') { | 
|                     //  | 
|                     this.x = uni.$u.range(0, left + width, e.deltaX + this.x) | 
|                     // 根据偏移值,得出移动的百分比,进而修改双向绑定的v-model的值 | 
|                     const value = (this.x / width) * 100 | 
|                     const percent = this.formatStep(value) | 
|                     // 修改value值 | 
|                     this.$emit('input', percent) | 
|                     // 标记下一次触发value的watch时,这个值的变化,是由内部改变的 | 
|                     this.changeFromInside = true | 
|                     this.moving = false | 
|                     this.touching = false | 
|                 } | 
|             }) | 
|         }, | 
|         // 从value的变化,倒推得出x的值该为多少 | 
|         initX() { | 
|             const { | 
|                 left, | 
|                 width | 
|             } = this.sliderRect | 
|             // 得出x的初始偏移值,之所以需要这么做,是因为在bindingX中,触摸滑动时,只能的值本次移动的偏移值 | 
|             // 而无法的值准确的前后移动的两个点的坐标值,weex纯粹为阿里巴巴的KPI(部门业绩考核)产物,也就这样了 | 
|             this.x = this.value / 100 * width | 
|             // 设置移动的值 | 
|             const barStyle = { | 
|                 width: this.x + 'px' | 
|             } | 
|             // 按钮的初始值 | 
|             const buttonWrapperStyle = { | 
|                 transform: `translateX(${this.x - this.blockHeight / 2}px)` | 
|             } | 
|             this.initButtonStyle({ | 
|                 barStyle, | 
|                 buttonWrapperStyle | 
|             }) | 
|         } | 
|     } | 
| } |