| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- <template>
- <view class="l-switch">
- <view
- class="l-switch__rail"
- ref="rootRef"
- :hover-start-time="20"
- :hover-stay-time="70"
- :hover-class="hoverClass"
- :class="classes"
- :style="[styles]"
- aria-role="switch"
- @touchstart="touchstart"
- @touchend="touchend"
- @touchcancel="touchend"
- @click="handleClick">
- <!-- #ifndef APP-ANDROID || APP-IOS -->
- <view class="l-switch__children-placeholder" v-if="placeholder.length > 0">
- <text class="l-switch__rail-placeholder" v-for="item in placeholder" :key="item">
- {{item}}
- </text>
- </view>
- <!-- #endif -->
-
- <view class="l-switch__dot" :class="dotClass" :style="[dotStyle]" ref="dotRef">
- <text class="l-switch__placeholder l-switch__placeholder--checked"
- ref="checkedRef" v-if="placeholder.length > 0">
- <!-- <slot name="checked"></slot> -->
- {{placeholder[0]}}
- </text>
- <text class="l-switch__placeholder l-switch__placeholder--unchecked"
- ref="uncheckedRef" v-if="placeholder.length > 1">
- <!-- <slot name="unchecked"></slot> -->
- {{placeholder[1]}}
- </text>
- <!-- #ifndef APP-ANDROID || APP-IOS -->
- <l-loading v-if="loading && $slots['icon'] == null"></l-loading>
- <!-- #endif -->
- <slot name="icon" :checked="innerValue"></slot>
- </view>
- </view>
- </view>
- </template>
-
- <script lang="uts" setup>
- /**
- * LimeSwitch 图片
- * @description 用于控制某个功能的开启和关闭。可嵌套内容、禁用以及颜色配置,兼容uniapp/uniappx
- * @tutorial https://ext.dcloud.net.cn/plugin?id=155222
- * @property {Boolean} value 选中时对应的值
- * @property {Boolean} defaultValue 默认值
- * @property {Boolean} disabled 是否禁用
- * @property {Boolean} loading 是否加载
- * @property {Boolean} round 是否为圆形
- * @property {Boolean} rubberBand 是否开启橡皮效果
- * @property {Array} placeholder 占位内容
- * @property {String} src 图片地址
- * @property {String} fontSize 字体大小
- * @property {String} width 最小宽度
- * @property {String} height 高度
- * @property {String} dotSize 按钮大小
- * @property {String} dotPressedSize 按下按钮大小
- * @property {String} checkedColor 激活背景色
- * @property {String} disabledColor 禁用背景色
- * @property {String} checkedDisabledColor 激活禁用背景色
- * @property {String} uncheckedColor 背景色
- * @event {Function} change 切换触发
- */
-
- import { SwitchProps } from './type';
- import { unitConvert } from '@/uni_modules/lime-shared/unitConvert';
- // #ifdef APP-ANDROID || APP-IOS
- import { useLoading } from '@/uni_modules/lime-loading'
- // #endif
- // #ifdef APP-ANDROID
- import Paint from 'android.graphics.Paint'
- // #endif
-
- const name = 'l-switch'
- const slots = defineSlots<{
- icon(props : { checked : boolean }) : any,
- }>();
- const emit = defineEmits(['change', 'update:modelValue'])
- const props = withDefaults(defineProps<SwitchProps>(), {
- disabled: false,
- loading: false,
- rubberBand: true,
- round: true,
- placeholder: [] as string[]
- })
- // 设置默认值
- const modelValue = ref((props.modelValue ?? false) || (props.defaultValue ?? false))
- const innerValue = computed({
- set(value: boolean){
- if(value == modelValue.value) return
- modelValue.value = value;
- emit('change', value)
- emit('update:modelValue', value)
- },
- get():boolean {
- return (props.value ?? false) || modelValue.value
- },
- } as WritableComputedOptions<boolean>)
-
- const classes = computed(():Map<string, boolean> =>{
- const cls = new Map<string, boolean>();
- cls.set(`${name}--checked`, innerValue.value)
- cls.set(`${name}--unchecked`, !innerValue.value)
- cls.set(`${name}--disabled`, props.disabled)
- cls.set(`${name}--round`, props.round)
- cls.set(`${name}--square`, !props.round)
- return cls
- })
-
- const dotClass = computed(():Map<string, boolean> =>{
- const cls = new Map<string, boolean>();
- // cls.set(`${name}__dot--checked`, innerValue.value)
- // cls.set(`${name}__dot--unchecked`, !innerValue.value)
- cls.set(`${name}__dot--disabled`, props.disabled)
- cls.set(`${name}__dot--round`, props.round)
- cls.set(`${name}__dot--square`, !props.round)
- return cls
- })
- const hoverClass = computed(():string =>{
- return props.rubberBand && !props.disabled && !props.loading ? 'l-switch--hover' : ''
- })
- const styles = computed(():Map<string, any>=>{
- const style = new Map<string, any>();
- // #ifndef APP-ANDROID || APP-IOS
- if(props.width != null) {
- style.set('--l-switch-width', props.width!)
- }
- if(props.height != null) {
- style.set('--l-switch-height', props.height!)
- }
- if(props.fontSize != null) {
- style.set('--l-swtich-font-size', props.fontSize!)
- }
- if(props.dotSize != null) {
- style.set('--l-switch-dot-size', props.dotSize!)
- }
- if(props.dotPressedSize != null) {
- style.set('--l-switch-dot-size-pressed', props.dotPressedSize!)
- }
- if(props.checkedColor != null) {
- style.set('--l-switch-checked-color', props.checkedColor!)
- }
- if(props.checkedDisabledColor != null) {
- style.set('--l-switch-checked-disabled-color', props.checkedDisabledColor!)
- }
- if(props.disabledColor != null) {
- style.set('--l-switch-unchecked-disabled-color', props.disabledColor!)
- }
- if(props.uncheckedColor != null) {
- style.set('--l-switch-unchecked-color', props.uncheckedColor!)
- }
- // #endif
- // #ifdef APP-ANDROID || APP-IOS
- if(props.width != null) {
- style.set('min-width', props.width!)
- }
- if(props.height != null) {
- style.set('height', props.height!)
- }
- // #endif
- return style
- })
-
- const dotStyle = computed(():Map<string, any>=>{
- const style = new Map<string, any>();
- // #ifdef APP-ANDROID || APP-IOS
- if(props.dotSize != null) {
- style.set('width', props.dotSize!)
- style.set('height', props.dotSize!)
- }
- // #endif
- return style
- })
-
-
- const handleClick = (e: UniPointerEvent) => {
- if(props.disabled || props.loading) return
- innerValue.value = !innerValue.value;
- }
- // #ifndef APP-ANDROID || APP-IOS
- const touchstart = () => {}
- const touchend = () => {}
- // #endif
- // #ifdef APP-ANDROID || APP-IOS
- const rootRef = ref<UniElement|null>(null)
- const dotRef = ref<UniElement|null>(null)
- const checkedRef = ref<UniElement|null>(null)
- const uncheckedRef = ref<UniElement|null>(null)
- const placeholderWidth = ref(0);
- const rootBaseWidth = ref(0)
- const dotWidth = ref(0);
- const hover = ref(false)
- const dotPressedSize = computed(():number => unitConvert(props.dotPressedSize ?? 0))
-
-
- const touchstart = (e: UniTouchEvent) => {
- e.stopPropagation()
- if(dotRef.value == null || !props.rubberBand || props.disabled || props.loading) return
- hover.value = true
- }
- const touchend = () => {
- if(dotRef.value == null || !props.rubberBand || props.disabled || props.loading) return
- hover.value = false
- }
-
- const updatePlaceholderWidth = (res: DOMRect) => {
- if(res.width > placeholderWidth.value || props.placeholder.length == 1) {
- placeholderWidth.value = res.width
- }
- }
-
- // const {play, clear} = useLoading(dotRef, 'circular', props.checkedColor ?? '#3283ff', 0.8)
- const loading = useLoading(dotRef);
- loading.type = 'circular';
- loading.color = props.checkedColor ?? '#3283ff';
- loading.ratio = 0.8
-
- watchEffect(()=>{
- if(props.placeholder.length == 0) {
- placeholderWidth.value = 0;
- return
- }
-
- // #ifdef APP-IOS
- nextTick(()=>{
- checkedRef.value?.getBoundingClientRectAsync()?.then(updatePlaceholderWidth)
- uncheckedRef.value?.getBoundingClientRectAsync()?.then(updatePlaceholderWidth)
- })
- // #endif
- // 安卓4.41获取不到文本宽度
- // #ifdef APP-ANDROID
- props.placeholder.forEach(text => {
- // 创建一个Paint对象
- const paint = new Paint();
- const fontSize = unitConvert(props.fontSize ?? uni.rpx2px(28))
- paint.setTextSize(fontSize.toFloat());
- // 测量文本宽度
- const textWidth = paint.measureText(text);
- if(textWidth > placeholderWidth.value || props.placeholder.length == 1) {
- placeholderWidth.value = textWidth
- nextTick(()=>{
- checkedRef.value?.style.setProperty('width', textWidth + 4 + 'px')
- uncheckedRef.value?.style.setProperty('width', textWidth + 4 + 'px')
- })
- }
- })
- // #endif
- })
- watchEffect(()=>{
- const root = rootRef.value?.getBoundingClientRect();
- const dot = dotRef.value?.getBoundingClientRect();
- if (root == null || dot == null) return;
-
- const dotSize = hover.value ? Math.max(dotPressedSize.value, dot.height * 1.25) : dot.height;
- const offset = (root.height - dot.height) / 2
- const bgColor = innerValue.value
- ? props.disabled ? props.checkedDisabledColor : props.checkedColor
- : props.disabled ? props.disabledColor : props.uncheckedColor;
-
- let width = rootBaseWidth.value;
- if(placeholderWidth.value > 0) {
- width = placeholderWidth.value + root.height * 1.75
- rootRef.value!.style.setProperty('width', `${width}px`)
- }
-
-
-
- dotRef.value!.style.setProperty('left', `${innerValue.value ? width - dotSize - offset : offset}`)
- dotRef.value!.style.setProperty('width', `${dotSize}`)
- dotRef.value!.style.setProperty('top', `${offset}`)
- checkedRef.value?.style.setProperty('padding-right', `${hover.value && !innerValue.value ? dotSize * 1.25 : 1.15 * root.height - offset}`)
- uncheckedRef.value?.style.setProperty('padding-left', `${hover.value && innerValue.value ? dotSize * 1.25 : 1.15 * root.height - offset}`)
-
- if(props.loading && slots['icon'] == null) {
- loading.play()
- } else {
- loading.clear()
- }
-
- if(bgColor == null) return
- nextTick(()=>{
- rootRef.value!.style.setProperty('backgroundColor', bgColor)
- })
- })
-
- onMounted(()=>{
- nextTick(()=>{
- if(rootRef.value == null) return;
- rootRef.value!.getBoundingClientRectAsync()?.then(res => {
- rootBaseWidth.value = res.width;
- })
- })
- })
- // #endif
-
- </script>
- <style lang="scss">
- @import './index-u';
- </style>
|