开关(Switch)选择组件是一种用户界面元素,允许用户在两种状态(通常是开/关、是/否、启用/禁用等)之间进行切换。这种组件在移动应用、桌面软件、网页以及物联网设备中广泛应用。以下是对开关Switch选择组件的详细介绍:

一、基本概念

开关Switch选择组件通常由一个滑块和一个滑道组成。滑块是用户可以拖动的部分,而滑道是背景。用户可以通过拖动滑块或点按开关来改变其状态。

二、主要属性

  1. 状态:
    • 表示开关的当前状态,通常是一个布尔值(true/false或1/0)。
  2. 状态改变回调:
    • 当开关状态发生变化时调用的回调函数。
    • 该函数通常接收一个新的状态值作为参数。
  3. 启用/禁用:
    • 控制开关是否可用。
    • 当设置为禁用状态时,用户无法更改开关的状态。
  4. 文本标签:
    • 在某些实现中,可以为开关的打开和关闭状态设置文本标签。
    • 这些标签通常用于提供更清晰的指示或说明。
  5. 颜色(colors):
    • 自定义开关的颜色,包括滑块和滑道的颜色。
    • 某些框架允许为开关的不同状态(打开/关闭)设置不同的颜色。

三、组件扩展

基于uview类型的u-switch我们增加了有效文本、无效文本及颜色。扩展组件如下。

<template>
	<view
		class="u-switch"
		:class="[valueCom? 'u-switch--on' : '', disabled ? 'u-switch--disabled' : '']"
		@tap="onClick"
		:style="[switchStyle]"
	>
		<view
			class="u-switch__node node-class"
			:style="nodeStyle"
		>
			<u-loading
				:show="loading"
				class="u-switch__loading"
				:size="size * 0.6"
				:color="loadingColor"
			/>
		</view>
		<view v-if="activeText || inactiveText" class="u-switch__text" :class="{'u-switch__text-end':!valueCom}" >
			<text v-if="!valueCom" class="u-switch__text--inactive"  :style="{color:inactiveTextColor}">{{ inactiveText }}</text>
			<text v-else class="u-switch__text--active" :style="{color:activeTextColor}">{{ activeText }}</text>
		</view>
	</view>
</template>

<script>
/**
 * switch 开关选择器
 * @description 选择开关一般用于只有两个选择,且只能选其一的场景。
 * @tutorial https://www.uviewui.com/components/switch.html
 * @property {Boolean} loading 是否处于加载中(默认false)
 * @property {Boolean} disabled 是否禁用(默认false)
 * @property {String Number} size 开关尺寸,单位rpx(默认50)
 * @property {String} active-color 打开时的背景色(默认#19be6b)
 * @property {Boolean} inactive-color 关闭时的背景色(默认#ffffff)
 * @property {Boolean | Number | String} active-value 打开选择器时通过change事件发出的值(默认true)
 * @property {Boolean | Number | String} inactive-value 关闭选择器时通过change事件发出的值(默认false)
 * @event {Function} change 在switch打开或关闭时触发
 * @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
 */
export default {
	name: "u-switch",
	emits: ["update:modelValue", "input", "change"],
	props: {
		// 通过v-model双向绑定的值
		value: {
			type: [Number, String, Boolean],
			default: false
		},
		modelValue: {
			type: [Number, String, Boolean],
			default: false
		},
		// 是否为加载中状态
		loading: {
			type: Boolean,
			default: false
		},
		// 是否为禁用装填
		disabled: {
			type: Boolean,
			default: false
		},
		// 开关尺寸,单位rpx
		size: {
			type: [Number, String],
			default: 50
		},
		// 打开时的背景颜色
		activeColor: {
			type: String,
			default: "#19be6b"
		},
		// 关闭时的背景颜色
		inactiveColor: {
			type: String,
			default: "#ffffff"
		},
		// 是否使手机发生短促震动,目前只在iOS的微信小程序有效(2020-05-06)
		vibrateShort: {
			type: Boolean,
			default: false
		},
		// 打开选择器时的值
		activeValue: {
			type: [Number, String, Boolean],
			default: true
		},
		// 关闭选择器时的值
		inactiveValue: {
			type: [Number, String, Boolean],
			default: false
		},
		activeText: {
			type: String,
			default: ''
		},
		activeTextColor: {
			type: String,
			default: "#ffffff"
		},
		inactiveText: {
			type: String,
			default: ''
		},
		inactiveTextColor: {
			type: String,
			default: "#999999"
		},
	},
	data() {
		return {
			switchWidth:this.size,
		};
	},
	computed: {
		valueCom() {
			// #ifndef VUE3
			return this.value;
			// #endif

			// #ifdef VUE3
			return this.modelValue;
			// #endif
		},
		switchStyle() {
			let style = {};
			style.fontSize = this.size + "rpx";
			style.backgroundColor = this.valueCom ? this.activeColor : this.inactiveColor;
			style.width = this.$u.addUnit(this.switchWidth*2);
			return style;
		},
		loadingColor() {
			return this.valueCom ? this.activeColor : null;
		},
		nodeStyle(){
			const style = {};
			style.width = this.$u.addUnit(this.size);
			style.height = this.$u.addUnit(this.size);
			style.transform = `translateX(${this.valueCom ? this.switchWidth - this.size/2 : 0}px)`;
			return style;
		}
	},
	methods: {
		onClick() {
			if (!this.disabled && !this.loading) {
				// 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效
				if (this.vibrateShort) uni.vibrateShort();
				this.$emit("input", this.valueCom==this.activeValue ? this.inactiveValue : this.activeValue);
				this.$emit("update:modelValue", this.valueCom==this.activeValue ? this.inactiveValue : this.activeValue);
				// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
				this.$nextTick(() => {
					this.$emit("change", this.valueCom==this.activeValue ? this.inactiveValue : this.activeValu);
				});
			}
		},
		updateSwitchWidth() {
			let textLength = Math.max(this.activeText.length,this.inactiveText.length)
			this.switchWidth = Math.max(textLength*12+10+this.size/2, this.size)
		}
	},
	mounted() {
		this.updateSwitchWidth();
	},
};
</script>

<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";

.u-switch {
	position: relative;
	/* #ifndef APP-NVUE */
	display: inline-block;
	/* #endif */
	box-sizing: initial;
	width: 2em;
	height: 1em;
	background-color: #fff;
	border: 1px solid rgba(0, 0, 0, 0.1);
	border-radius: 1em;
	transition: background-color 0.3s;
	font-size: 50rpx;
	&__text {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0 10rpx;
		font-size: 24rpx;
		&-end{
			justify-content: flex-end;
		}
		&--inactive {
			color: #999999;
			white-space: nowrap;
			text-align: right;
		}
	
		&--active {
			color: #fff;
			white-space: nowrap;
		}
	}
}

.u-switch__node {
	@include vue-flex;
	align-items: center;
	justify-content: center;
	position: absolute;
	top: 0;
	left: 0;
	border-radius: 100%;
	z-index: 1;
	background-color: #fff;
	background-color: #fff;
	box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1),
		0 3px 3px 0 rgba(0, 0, 0, 0.05);
	box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1),
		0 3px 3px 0 rgba(0, 0, 0, 0.05);
	transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
	transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05),
		-webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
	transition: transform cubic-bezier(0.3, 1.05, 0.4, 1.05);
	transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
}

.u-switch__loading {
	@include vue-flex;
	align-items: center;
	justify-content: center;
}

.u-switch--on {
	background-color: #1989fa;
}

.u-switch--on .u-switch__node {
	transform: translateX(100%);
}

.u-switch--disabled {
	opacity: 0.4;
}
</style>

四、可视化设计

拖动开关组件进设计区。

保存源码至本地查看效果

<template>
	<view class="container container329152">
		<u-form-item class="diygw-col-24" label="开关" prop="switch">
			<view class="flex diygw-col-24">
				<u-switch :size="44" :activeValue="1" :inactiveValue="0" inactiveTextColor="#000000" activeTextColor="#ffffff" v-model="switched" slot="right"></u-switch>
			</view>
		</u-form-item>
		<u-form-item class="diygw-col-24" label="开关" prop="switch1">
			<view class="flex diygw-col-24">
				<u-switch :size="44" activeText="有效" inactiveText="无效" :activeValue="1" :inactiveValue="0" inactiveTextColor="#000000" activeTextColor="#ffffff" v-model="switch1" slot="right"></u-switch>
			</view>
		</u-form-item>
		<u-form-item class="diygw-col-24" label="开关" prop="switch2">
			<view class="flex diygw-col-24">
				<u-switch :size="44" activeText="男" inactiveText="女" :activeValue="1" :inactiveValue="0" inactiveTextColor="#000000" activeTextColor="#ffffff" v-model="switch2" slot="right"></u-switch>
			</view>
		</u-form-item>
		<view class="clearfix"></view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				//用户全局信息
				userInfo: {},
				//页面传参
				globalOption: {},
				//自定义全局变量
				globalData: {},
				listNum: 1,
				list: {
					code: 200,
					msg: '获取数据成功',
					data: [
						{
							title: '标题1',
							remark: '描述1',
							id: 1,
							attr: {
								title: '标题1'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题2',
							remark: '描述2',
							id: 2,
							attr: {
								title: '标题2'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题3',
							remark: '描述3',
							id: 3,
							attr: {
								title: '标题3'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题4',
							remark: '描述4',
							id: 4,
							attr: {
								title: '标题4'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题5',
							remark: '描述5',
							id: 5,
							attr: {
								title: '标题5'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题6',
							remark: '描述6',
							id: 6,
							attr: {
								title: '标题6'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题7',
							remark: '描述7',
							id: 7,
							attr: {
								title: '标题7'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题8',
							remark: '描述8',
							id: 8,
							attr: {
								title: '标题8'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题9',
							remark: '描述9',
							id: 9,
							attr: {
								title: '标题9'
							},
							img: 'https://php.diygw.com/logo.png'
						},
						{
							title: '标题10',
							remark: '描述10',
							id: 10,
							attr: {
								title: '标题10'
							},
							img: 'https://php.diygw.com/logo.png'
						}
					]
				},
				switched: 1,
				switch1: 1,
				switch2: 1
			};
		},
		onPageScroll(e) {
			const scrollTop = e.scrollTop;
			this.headerBackgroundStyle = this.headerBackgroundStyle || { background: 'none' };
			if (scrollTop <= 80) {
				const opacity = scrollTop / 100;
				const color = `rgba(255, 255, 255, ${opacity})`;
				this.headerBackgroundStyle.background = color;
			} else {
				this.headerBackgroundStyle.background = '#ffffff';
			}
		},
		onShow() {
			this.setCurrentPage(this);
		},
		onLoad(option) {
			this.setCurrentPage(this);
			if (option) {
				this.setData({
					globalOption: this.getOption(option)
				});
			}

			this.init();
		},
		methods: {
			async init() {
				await this.listApi();
			},
			// 列表数据 API请求方法
			async listApi(param) {
				let thiz = this;
				param = param || {};

				//如果请求要重置页面,请配置点击附加参数refresh=1  增加判断如输入框回调param不是对象
				if (param.refresh || typeof param != 'object') {
					this.listNum = 1;
				}

				//请求地址及请求数据,可以在加载前执行上面增加自己的代码逻辑
				let http_url = 'https://php.diygw.com/article.php';
				let http_data = {
					pageNum: this.listNum,
					pageSize: 10,
					sctdown: param.sctdown || this.sctdown
				};
				let http_header = {};

				let list = await this.$http.post(http_url, http_data, http_header, 'json');

				let datarows = list.rows;
				if (http_data.pageNum == 1) {
					this.list = list;
				} else if (datarows) {
					let rows = this.list.rows.concat(datarows);
					list.rows = rows;
					this.list = list;
				}
				if (datarows && datarows.length > 0) {
					this.listNum = this.listNum + 1;
				}
				this.globalData.isshow = true;
				console.log(http_data.sctdown);
			}
		},
		onPullDownRefresh() {
			// 列表数据 API请求方法
			this.listNum = 1;
			this.listApi();

			uni.stopPullDownRefresh();
		},
		onReachBottom() {
			// 列表数据 API请求方法
			this.listApi();
		}
	};
</script>

<style lang="scss" scoped>
	.container329152 {
	}
</style>

Logo

低代码爱好者的网上家园

更多推荐