starmusic-pc
1<script setup>
2/**
3 * UiSwitch — 开关
4 * 用主题色表示开启态,滑块带轻微过冲动画。
5 */
6const props = defineProps({
7 modelValue: { type: Boolean, default: false },
8 disabled: { type: Boolean, default: false },
9 size: { type: String, default: 'md' }, // sm | md
10})
11
12const emit = defineEmits(['update:modelValue', 'change'])
13
14const toggle = () => {
15 if (props.disabled) return
16 const next = !props.modelValue
17 emit('update:modelValue', next)
18 emit('change', next)
19}
20</script>
21
22<template>
23 <button
24 type="button"
25 class="switch"
26 :class="[`size-${size}`, { 'is-on': modelValue, 'is-disabled': disabled }]"
27 role="switch"
28 :aria-checked="modelValue"
29 :disabled="disabled"
30 @click="toggle"
31 >
32 <span class="knob" />
33 </button>
34</template>
35
36<style scoped>
37.switch {
38 position: relative;
39 flex: none;
40 width: 42px;
41 height: 24px;
42 border-radius: var(--r-full);
43 background: var(--surface-4);
44 transition: background-color var(--t-base) var(--ease-out), box-shadow var(--t-base) var(--ease-out);
45}
46
47.switch.size-sm {
48 width: 34px;
49 height: 20px;
50}
51
52.switch.is-on {
53 background: var(--theme-color);
54 box-shadow: 0 2px 8px rgba(var(--theme-color-rgb), 0.32);
55}
56
57.switch.is-disabled {
58 opacity: 0.5;
59 cursor: not-allowed;
60}
61
62.knob {
63 position: absolute;
64 top: 2px;
65 left: 2px;
66 width: 20px;
67 height: 20px;
68 border-radius: var(--r-full);
69 background: #fff;
70 box-shadow: 0 1px 3px rgba(20, 25, 32, 0.2);
71 transition: transform var(--t-base) var(--ease-overshoot);
72}
73
74.size-sm .knob {
75 width: 16px;
76 height: 16px;
77}
78
79.switch.is-on .knob {
80 transform: translateX(18px);
81}
82
83.switch.size-sm.is-on .knob {
84 transform: translateX(14px);
85}
86
87.switch:active:not(.is-disabled) .knob {
88 transform: scale(0.94) translateX(0);
89}
90
91.switch.is-on:active:not(.is-disabled) .knob {
92 transform: scale(0.94) translateX(18px);
93}
94
95.switch.size-sm.is-on:active:not(.is-disabled) .knob {
96 transform: scale(0.94) translateX(14px);
97}
98</style>