微信小程序 实现滑块是矩形的slider组件

我发现大多数前端UI库都是圆形的滑块,而且圆形的滑块都没有紧贴进度条,都是超出了首尾端,所以亲自写一个矩形的滑块,我使用了微信小程序的wxs的事件通信写法,官方说这样写好,也不知道好哪里了。样式如下图:

js

// components/slider/index.js
Component({
	// 启用插槽
	options: {
		multipleSlots: true
	},
	/**
	 * 组件的属性列表
	 */
	properties: {

		disabled: {
			type: Boolean,
			value: false,
			observer(val, oldVal) {
				if (val !== this.value) {
					this.setData({
						['propValue.disabled']: val
					})
				}
			},
		},
		max: {
			type: Number,
			value: 100,
			observer(val, oldVal) {
				if (val !== this.value) {
					this.setData({
						['propValue.max']: val
					})
				}
			},
		},
		min: {
			type: Number,
			value: 0,
			observer(val, oldVal) {
				if (val !== this.value) {
					this.setData({
						['propValue.min']: val
					})
				}
			},
		},
		step: {
			type: Number,
			value: 1,
			observer(val, oldVal) {
				if (val !== this.value) {
					this.setData({
						['propValue.step']: val
					})
				}
			},
		},
		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 {{disabled?'n-slider__disabled':''}}" 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 disabled = propValue.disabled;
	if (disabled) {
		return;
	}

	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);
	ins.selectComponent('.n-slider-handle-wrapper').setStyle({
		"left": getOffsetWidth(value),
		"transition": drag ? "none" : "all .2s"
	});

	ins.selectComponent('.n-slider-rail__fill').setStyle({
		"width": getOffsetWidth(value),
		"transition": drag ? "none" : "all .2s"
	});

	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) {

	//这里判断一下newValue.value是否等于0,否则等于0的情况下不会执行这里
	if (newValue.value || newValue.value === 0) {
		propValue = newValue;
		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__disabled {
	opacity: .5;
}

.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;
	/* transition: all .2s; */
}

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

如果觉得可以,请微信关注《华音有声剧社》公众号,进入小程序听书

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用微信小程序的 `slider` 组件和 `image` 组件实现双向 slider 加背景图展示的效果。 首先,需要在 `wxml` 文件中添加 `slider` 和 `image` 组件,代码如下: ```html <view class="container"> <image class="background" src="{{backgroundUrl}}"></image> <slider class="slider" value="{{value1}}" min="{{min}}" max="{{max}}" bindchange="onChange"></slider> <slider class="slider" value="{{value2}}" min="{{min}}" max="{{max}}" bindchange="onChange"></slider> </view> ``` 其中 `background` 类设置背景图的样式,`slider` 类设置 slider 的样式。 然后,在 `js` 文件中定义 `data`,并在 `onLoad` 函数中设置背景图: ```javascript Page({ data: { backgroundUrl: '/images/background.jpg', value1: 20, value2: 80, min: 0, max: 100, }, onLoad: function () { wx.getImageInfo({ src: this.data.backgroundUrl, success: (res) => { this.setData({ backgroundWidth: res.width, backgroundHeight: res.height, }) }, }) }, onChange: function (event) { const { value } = event.detail const { dataset } = event.currentTarget const { index } = dataset if (index === '1') { this.setData({ value1: value, }) } else { this.setData({ value2: value, }) } }, }) ``` 在 `onChange` 函数中,通过 `dataset` 获取 slider 的索引,然后根据索引更新对应的 `value`。 最后,在 `wxss` 文件中设置样式: ```css .container { position: relative; } .background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; } .slider { position: absolute; width: 80%; left: 10%; z-index: 1; } ``` 其中,`background` 样式设置背景图的样式,`slider` 样式设置 slider 的样式,并通过 `z-index` 属性设置层级关系,保证 slider 在背景图上方。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值