u-switch.vue
6.0 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<template>
<view
class="u-switch"
:class="[disabled && 'u-switch--disabled']"
:style="[switchStyle, $u.addStyle(customStyle)]"
@tap="clickHandler"
>
<view
class="u-switch__bg"
:style="[bgStyle]"
>
</view>
<view
class="u-switch__node"
:class="[value && 'u-switch__node--on']"
:style="[nodeStyle]"
ref="u-switch__node"
>
<u-loading-icon
:show="loading"
mode="circle"
timingFunction='linear'
:color="value ? activeColor : '#AAABAD'"
:size="size * 0.6"
/>
</view>
</view>
</template>
<script>
import props from './props.js';
/**
* switch 开关选择器
* @description 选择开关一般用于只有两个选择,且只能选其一的场景。
* @tutorial https://www.uviewui.com/components/switch.html
* @property {Boolean} loading 是否处于加载中(默认 false )
* @property {Boolean} disabled 是否禁用(默认 false )
* @property {String | Number} size 开关尺寸,单位px (默认 25 )
* @property {String} activeColor 打开时的背景色 (默认 '#2979ff' )
* @property {String} inactiveColor 关闭时的背景色 (默认 '#ffffff' )
* @property {Boolean | String | Number} value 通过v-model双向绑定的值 (默认 false )
* @property {Boolean | String | Number} activeValue 打开选择器时通过change事件发出的值 (默认 true )
* @property {Boolean | String | Number} inactiveValue 关闭选择器时通过change事件发出的值 (默认 false )
* @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false )
* @property {String | Number} space 圆点与外边框的距离 (默认 0 )
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} change 在switch打开或关闭时触发
* @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
*/
export default {
name: "u-switch",
mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
watch: {
value: {
immediate: true,
handler(n) {
if(n !== this.inactiveValue && n !== this.activeValue) {
uni.$u.error('v-model绑定的值必须为inactiveValue、activeValue二者之一')
}
}
}
},
data() {
return {
bgColor: '#ffffff'
}
},
computed: {
isActive(){
return this.value === this.activeValue;
},
switchStyle() {
let style = {}
// 这里需要加2,是为了腾出边框的距离,否则圆点node会和外边框紧贴在一起
style.width = uni.$u.addUnit(this.size * 2 + 2)
style.height = uni.$u.addUnit(Number(this.size) + 2)
// style.borderColor = this.value ? 'rgba(0, 0, 0, 0)' : 'rgba(0, 0, 0, 0.12)'
// 如果自定义了“非激活”演示,name边框颜色设置为透明(跟非激活颜色一致)
// 这里不能简单的设置为非激活的颜色,否则打开状态时,会有边框,所以需要透明
if(this.customInactiveColor) {
style.borderColor = 'rgba(0, 0, 0, 0)'
}
style.backgroundColor = this.isActive ? this.activeColor : this.inactiveColor
return style;
},
nodeStyle() {
let style = {}
// 如果自定义非激活颜色,将node圆点的尺寸减少两个像素,让其与外边框距离更大一点
style.width = uni.$u.addUnit(this.size - this.space)
style.height = uni.$u.addUnit(this.size - this.space)
const translateX = this.isActive ? uni.$u.addUnit(this.space) : uni.$u.addUnit(this.size);
style.transform = `translateX(-${translateX})`
return style
},
bgStyle() {
let style = {}
// 这里配置一个多余的元素在HTML中,是为了让switch切换时,有更良好的背景色扩充体验(见实际效果)
style.width = uni.$u.addUnit(Number(this.size) * 2 - this.size / 2)
style.height = uni.$u.addUnit(this.size)
style.backgroundColor = this.inactiveColor
// 打开时,让此元素收缩,否则反之
style.transform = `scale(${this.isActive ? 0 : 1})`
return style
},
customInactiveColor() {
// 之所以需要判断是否自定义了“非激活”颜色,是为了让node圆点离外边框更宽一点的距离
return this.inactiveColor !== '#fff' && this.inactiveColor !== '#ffffff'
}
},
methods: {
clickHandler() {
if (!this.disabled && !this.loading) {
const oldValue = this.isActive ? this.inactiveValue : this.activeValue
if(!this.asyncChange) {
this.$emit('input', oldValue)
}
// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
this.$nextTick(() => {
this.$emit('change', oldValue)
})
}
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-switch {
@include flex(row);
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: relative;
background-color: #fff;
border-width: 1px;
border-radius: 100px;
transition: background-color 0.4s;
border-color: rgba(0, 0, 0, 0.12);
border-style: solid;
justify-content: flex-end;
align-items: center;
// 由于weex为阿里逗着玩的KPI项目,导致bug奇多,这必须要写这一行,
// 否则在iOS上,点击页面任意地方,都会触发switch的点击事件
overflow: hidden;
&__node {
@include flex(row);
align-items: center;
justify-content: center;
border-radius: 100px;
background-color: #fff;
border-radius: 100px;
box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.25);
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: cubic-bezier(0.3, 1.05, 0.4, 1.05);
}
&__bg {
position: absolute;
border-radius: 100px;
background-color: #FFFFFF;
transition-property: transform;
transition-duration: 0.4s;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
transition-timing-function: ease;
}
&--disabled {
opacity: 0.6;
}
}
</style>