我发现大多数前端UI库都是圆形的滑块,而且圆形的滑块都没有紧贴进度条,都是超出了首尾端,所以亲自写一个矩形的滑块,我使用了微信小程序的wxs的事件通信写法,官方说这样写好,也不知道好哪里了。样式如下图:
js
// components/slider/index.js
Component({
// 启用插槽
options: {
multipleSlots: true
},
/**
* 组件的属性列表
*/
properties: {
disabled: {
type: Boolean,
value: false,
},
max: {
type: Number,
value: 100,
},
min: {
type: Number,
value: 0,
},
step: {
type: Number,
value: 1,
},
value: {
type: null,
value: 0,
observer(val, oldVal) {
if (val !== this.value) {
this.setData({
['propValue.value']: val
})
}
},
}
},
/**
* 组件的初始数据
*/
data: {
propValue: {
disabled: null,
max: null,
min: null,
step: null,
value: null
}
},
lifetimes: {
attached() {
this.data.propValue.disabled = this.data.disabled;
this.data.propValue.max = this.data.max;
this.data.propValue.min = this.data.min;
this.data.propValue.step = this.data.step;
this.data.propValue.value = this.data.value;
this.setData({
propValue: this.data.propValue
})
}
},
/**
* 组件的方法列表
*/
methods: {
setVal(val) {
// this.setData({
// value: val
// })
}
}
})
wxml
<!--自定义滑块组件-->
<wxs module="computed" src="./index.wxs"></wxs>
<view class="n-slider" change:prop="{{computed.propObserver}}" prop="{{propValue}}" bind:tap="{{computed.onClick}}" bind:touchstart="{{computed.onTouchStart}}" catch:touchmove="{{computed.onTouchMove}}" bind:touchend="{{computed.onTouchEnd}}" bind:touchcancel="{{computed.onTouchEnd}}">
<view class="n-slider-rail">
<view class="n-slider-rail__fill"></view>
<view class="n-slider-handles">
<view class="n-slider-handle-wrapper">
<view class="n-slider-handle">
<slot></slot>
</view>
</view>
</view>
</view>
</view>
wxs
var DRAG_STATUS = {
START: 'start',
MOVING: 'moving',
END: 'end',
};
var dragStatus = DRAG_STATUS.START;
var propValue = {
disabled: false,
max: 100,
min: 0,
step: 1,
value: 0
}
var newValue;
function onTouchStart(event, ins) {
var disabled = propValue.disabled;
if (disabled) {
return;
}
newValue = propValue.value;
dragStatus = DRAG_STATUS.START;
}
function onTouchMove(event, ins) {
var disabled = propValue.disabled;
if (disabled) {
return;
}
if (dragStatus === DRAG_STATUS.START) {
ins.triggerEvent('drag-start');
}
dragStatus = DRAG_STATUS.MOVING;
var touch = event.touches[0];
var rect = ins.selectComponent('.n-slider-handles').getBoundingClientRect();
var percentage = (touch.clientX - rect.left) / rect.width;
newValue = percentageToValue(percentage);
updateValue(ins, newValue, false, true);
}
function onTouchEnd(event, ins) {
var disabled = propValue.disabled;
if (disabled) {
return;
}
if (dragStatus === DRAG_STATUS.MOVING) {
dragStatus = DRAG_STATUS.END;
updateValue(ins, newValue, true);
ins.triggerEvent('drag-end');
}
}
function onClick(event, ins) {
var touch = event.touches[0];
var rect = ins.selectComponent('.n-slider-handles').getBoundingClientRect();
var percentage = (touch.clientX - rect.left) / rect.width;
updateValue(ins, percentageToValue(percentage), true);
}
/**
* 限制拖动值不能超出最小和最大范围
* @param value
*/
function format(value) {
return Math.round(Math.max(propValue.min, Math.min(value, propValue.max)) / propValue.step) * propValue.step;
}
/**
*
* @param ins
* @param val 不能和value重复
* @param end
* @param drag
*/
function updateValue(ins, val, end, drag) {
value = format(val);
console.log(val);
ins.selectComponent('.n-slider-handle-wrapper').setStyle({
"left": getOffsetWidth(value),
});
ins.selectComponent('.n-slider-rail__fill').setStyle({
"width": getOffsetWidth(value),
});
if (drag) {
ins.triggerEvent('drag', {
value: value
});
}
if (end) {
ins.triggerEvent('change', value);
}
if (drag || end) {
// ins.callMethod('setVal', value);
}
}
function getScope() {
return Number(propValue.max) - Number(propValue.min);
}
function getOffsetWidth(current) {
var scope = getScope();
// 避免最小值小于最小step时出现负数情况
return "".concat(Math.max(((current - Number(propValue.min)) * 100) / scope, 0), "%");
}
function percentageToValue(percentage) {
return propValue.min + (propValue.max - propValue.min) * percentage
}
function propObserver(newValue, oldValue, ownerInstance, instance) {
if (newValue.value) {
propValue = newValue;
console.log('propValue.value:' + propValue.value);
updateValue(ownerInstance, propValue.value);
}
}
module.exports = {
onTouchStart: onTouchStart,
onTouchMove: onTouchMove,
onTouchEnd: onTouchEnd,
onClick: onClick,
propObserver: propObserver
}
wxss
.n-slider {
position: relative;
z-index: 0;
width: 100%;
/* 增大点击范围 */
padding: 7px 0;
}
.n-slider .n-slider-rail {
width: 100%;
position: relative;
height: 10rpx;
background-color: rgb(219, 219, 223);
border-radius: 10rpx;
}
.n-slider .n-slider-rail .n-slider-rail__fill {
position: absolute;
top: 0;
bottom: 0;
background-color: #FA6800;
left: 0;
/* 改变这个 */
width: 0%;
border-radius: inherit;
}
.n-slider .n-slider-handles {
position: absolute;
top: 0;
bottom: 0;
/* 这里是滑块宽度的一半 */
left: 80rpx;
right: 80rpx;
}
.n-slider .n-slider-handles .n-slider-handle-wrapper {
outline: none;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
/* 改变这个 */
left: 0%;
z-index: 0;
}
.n-slider .n-slider-handles .n-slider-handle-wrapper .n-slider-handle {
height: 42rpx;
line-height: 42rpx;
width: 160rpx;
overflow: hidden;
background-color: #FFFFFF;
/* box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.3), inset 0 0 1px 0 rgba(0, 0, 0, 0.05); */
opacity: .8;
font-size: 26rpx;
text-align: center;
border: 1px solid #b9b9b9;
box-sizing: border-box;
border-radius: 6rpx;
color: #333333;
}
如果觉得可以,请微信关注《华音有声剧社》公众号,进入小程序听书