From 026134ae7c31071bc982556f326af014223c30c5 Mon Sep 17 00:00:00 2001 From: handongxun Date: Wed, 9 Feb 2022 17:18:28 +0800 Subject: [PATCH] feat(nvue): progress --- .../uni-components/src/components/progress.ts | 61 +++++++ .../uni-components/src/nvue/components.ts | 2 + .../src/nvue/progress/index.tsx | 161 ++++++++++++++++++ .../uni-components/src/vue/progress/index.tsx | 65 +------ 4 files changed, 230 insertions(+), 59 deletions(-) create mode 100644 packages/uni-components/src/components/progress.ts create mode 100644 packages/uni-components/src/nvue/progress/index.tsx diff --git a/packages/uni-components/src/components/progress.ts b/packages/uni-components/src/components/progress.ts new file mode 100644 index 000000000..9a2760df5 --- /dev/null +++ b/packages/uni-components/src/components/progress.ts @@ -0,0 +1,61 @@ +import { PRIMARY_COLOR } from '@dcloudio/uni-shared' + +const FONT_SIZE = 16 + +export const PROGRESS_VALUES = { + activeColor: PRIMARY_COLOR, + backgroundColor: '#EBEBEB', + activeMode: 'backwards', +} + +export const progressProps = { + percent: { + type: [Number, String], + default: 0, + validator(value: number | string) { + return !isNaN(parseFloat(value as string)) + }, + }, + fontSize: { + type: [String, Number], + default: FONT_SIZE, + }, + showInfo: { + type: [Boolean, String], + default: false, + }, + strokeWidth: { + type: [Number, String], + default: 6, + validator(value: number | string) { + return !isNaN(parseFloat(value as string)) + }, + }, + color: { + type: String, + default: PROGRESS_VALUES.activeColor, + }, + activeColor: { + type: String, + default: PROGRESS_VALUES.activeColor, + }, + backgroundColor: { + type: String, + default: PROGRESS_VALUES.backgroundColor, + }, + active: { + type: [Boolean, String], + default: false, + }, + activeMode: { + type: String, + default: PROGRESS_VALUES.activeMode, + }, + duration: { + type: [Number, String], + default: 30, + validator(value: number | string) { + return !isNaN(parseFloat(value as string)) + }, + }, +} diff --git a/packages/uni-components/src/nvue/components.ts b/packages/uni-components/src/nvue/components.ts index f752bf2c4..cf96aca5e 100644 --- a/packages/uni-components/src/nvue/components.ts +++ b/packages/uni-components/src/nvue/components.ts @@ -3,10 +3,12 @@ import Label from './label' import Button from './button' import MovableArea from './movable-area' import MovableView from './movable-view' +import Progress from './progress' export default { Navigator, Label, Button, MovableArea, MovableView, + Progress, } diff --git a/packages/uni-components/src/nvue/progress/index.tsx b/packages/uni-components/src/nvue/progress/index.tsx new file mode 100644 index 000000000..8be1815fb --- /dev/null +++ b/packages/uni-components/src/nvue/progress/index.tsx @@ -0,0 +1,161 @@ +import { + defineComponent, + Ref, + ref, + reactive, + watch, + computed, + onMounted, + ExtractPropTypes, +} from 'vue' +import { + useCustomEvent, + EmitEvent, + CustomEventTrigger, +} from '../../helpers/useNvueEvent' +import { getComponentSize } from '../helpers' +import { createNVueTextVNode } from '../utils' +import { PROGRESS_VALUES, progressProps } from '../../components/progress' + +const progressStyles: Record>[] = [ + { + 'uni-progress': { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + }, + 'uni-progress-bar': { + flex: 1, + }, + 'uni-progress-inner-bar': { + position: 'absolute', + }, + 'uni-progress-info': { + marginLeft: '15px', + }, + }, +] + +type ProgressProps = ExtractPropTypes +type ProgerssState = ReturnType + +export default defineComponent({ + name: 'Progress', + props: progressProps, + styles: progressStyles, + emits: ['activeend'], + setup(props, { emit }) { + const progressRef: Ref = ref(null) + const progressBarRef: Ref = ref(null) + const trigger = useCustomEvent>(progressRef, emit) + + const state = useProgressState(props) + + watch( + () => state.realPercent, + (newValue, oldValue) => { + state.lastPercent = oldValue || 0 + _activeAnimation(state, props, trigger) + } + ) + + onMounted(() => { + setTimeout(() => { + getComponentSize(progressBarRef.value!).then(({ width }) => { + state.progressWidth = width || 0 + _activeAnimation(state, props, trigger) + }) + }, 50) + }) + + return () => { + const { showInfo, fontSize } = props + const { outerBarStyle, innerBarStyle, currentPercent } = state + return ( +
+
+
+
+ {showInfo + ? createNVueTextVNode(currentPercent + '%', { + class: 'uni-progress-info', + style: { + fontSize, + }, + }) + : null} +
+ ) + } + }, +}) + +function useProgressState(props: ProgressProps) { + const currentPercent = ref(0) + const progressWidth = ref(0) + + const outerBarStyle = computed(() => ({ + backgroundColor: props.backgroundColor, + height: props.strokeWidth, + })) + const innerBarStyle = computed(() => { + // 兼容下不推荐的属性,activeColor 优先级高于 color。 + // nvue 不支持百分比,宽度必须为数值。 + const backgroundColor = + props.color !== PROGRESS_VALUES.activeColor && + props.activeColor === PROGRESS_VALUES.activeColor + ? props.color + : props.activeColor + return { + width: (currentPercent.value * progressWidth.value) / 100, + height: props.strokeWidth, + backgroundColor: backgroundColor, + } + }) + const realPercent = computed(() => { + // 确保最终计算时使用的是 Number 类型的值,并且在有效范围内。 + let realValue = parseFloat(props.percent as string) + realValue < 0 && (realValue = 0) + realValue > 100 && (realValue = 100) + return realValue + }) + + const state = reactive({ + outerBarStyle, + innerBarStyle, + realPercent, + + currentPercent, + strokeTimer: 0, + lastPercent: 0, + progressWidth, + }) + return state +} + +function _activeAnimation( + state: ProgerssState, + props: ProgressProps, + trigger: CustomEventTrigger +) { + state.strokeTimer && clearInterval(state.strokeTimer) + if (props.active) { + state.currentPercent = + props.activeMode === PROGRESS_VALUES.activeMode ? 0 : state.lastPercent + state.strokeTimer = setInterval(() => { + if (state.currentPercent + 1 > state.realPercent) { + state.currentPercent = state.realPercent + state.strokeTimer && clearInterval(state.strokeTimer) + trigger('activeend', {}) + } else { + state.currentPercent += 1 + } + }, parseFloat(props.duration as string)) as unknown as number + } else { + state.currentPercent = state.realPercent + } +} diff --git a/packages/uni-components/src/vue/progress/index.tsx b/packages/uni-components/src/vue/progress/index.tsx index 19eec5185..914e3b9a2 100644 --- a/packages/uni-components/src/vue/progress/index.tsx +++ b/packages/uni-components/src/vue/progress/index.tsx @@ -1,68 +1,15 @@ -import { PRIMARY_COLOR } from '@dcloudio/uni-shared' import { ref, reactive, watch, computed, ExtractPropTypes } from 'vue' import { defineBuiltInComponent } from '../../helpers/component' -const VALUES = { - activeColor: PRIMARY_COLOR, - backgroundColor: '#EBEBEB', - activeMode: 'backwards', -} - -const props = { - percent: { - type: [Number, String], - default: 0, - validator(value: number | string) { - return !isNaN(parseFloat(value as string)) - }, - }, - showInfo: { - type: [Boolean, String], - default: false, - }, - strokeWidth: { - type: [Number, String], - default: 6, - validator(value: number | string) { - return !isNaN(parseFloat(value as string)) - }, - }, - color: { - type: String, - default: VALUES.activeColor, - }, - activeColor: { - type: String, - default: VALUES.activeColor, - }, - backgroundColor: { - type: String, - default: VALUES.backgroundColor, - }, - active: { - type: [Boolean, String], - default: false, - }, - activeMode: { - type: String, - default: VALUES.activeMode, - }, - duration: { - type: [Number, String], - default: 30, - validator(value: number | string) { - return !isNaN(parseFloat(value as string)) - }, - }, -} +import { PROGRESS_VALUES, progressProps } from '../../components/progress' -type ProgressProps = ExtractPropTypes +type ProgressProps = ExtractPropTypes type ProgerssState = ReturnType export default /*#__PURE__*/ defineBuiltInComponent({ name: 'Progress', - props, + props: progressProps, setup(props) { const state = useProgressState(props) @@ -107,8 +54,8 @@ function useProgressState(props: ProgressProps) { const innerBarStyle = computed(() => { // 兼容下不推荐的属性,activeColor 优先级高于 color。 const backgroundColor = - props.color !== VALUES.activeColor && - props.activeColor === VALUES.activeColor + props.color !== PROGRESS_VALUES.activeColor && + props.activeColor === PROGRESS_VALUES.activeColor ? props.color : props.activeColor return `width: ${currentPercent.value}%;background-color: ${backgroundColor}` @@ -136,7 +83,7 @@ function useProgressState(props: ProgressProps) { function _activeAnimation(state: ProgerssState, props: ProgressProps) { if (props.active) { state.currentPercent = - props.activeMode === VALUES.activeMode ? 0 : state.lastPercent + props.activeMode === PROGRESS_VALUES.activeMode ? 0 : state.lastPercent state.strokeTimer = setInterval(() => { if (state.currentPercent + 1 > state.realPercent) { state.currentPercent = state.realPercent -- GitLab