将应用主色调从 '#BBF246' 更改为 '#87CEEB'(天空蓝),并更新所有相关组件和页面中的颜色引用。同时为多个页面添加统一的渐变背景,提升视觉效果和用户体验。新增压力分析模态框组件,并优化压力计组件的交互与显示逻辑。更新应用图标和启动图资源。
108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import { Colors } from '@/constants/Colors';
|
|
import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
import { Animated, Easing, LayoutChangeEvent, StyleSheet, Text, View, ViewStyle } from 'react-native';
|
|
|
|
type ProgressBarProps = {
|
|
progress: number; // 0 - 1
|
|
height?: number;
|
|
style?: ViewStyle;
|
|
trackColor?: string;
|
|
fillColor?: string;
|
|
animated?: boolean;
|
|
showLabel?: boolean;
|
|
};
|
|
|
|
function ProgressBarImpl({
|
|
progress,
|
|
height = 18,
|
|
style,
|
|
trackColor = '#EDEDED',
|
|
fillColor = Colors.light.accentGreen,
|
|
animated = true,
|
|
showLabel = true,
|
|
}: ProgressBarProps) {
|
|
const [trackWidth, setTrackWidth] = useState(0);
|
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
|
|
const clamped = useMemo(() => {
|
|
if (Number.isNaN(progress)) return 0;
|
|
return Math.min(1, Math.max(0, progress));
|
|
}, [progress]);
|
|
|
|
useEffect(() => {
|
|
if (!animated) {
|
|
animatedValue.setValue(clamped);
|
|
return;
|
|
}
|
|
Animated.timing(animatedValue, {
|
|
toValue: clamped,
|
|
duration: 650,
|
|
easing: Easing.out(Easing.cubic),
|
|
useNativeDriver: false,
|
|
}).start();
|
|
}, [clamped, animated, animatedValue]);
|
|
|
|
const lastWidthRef = useRef<number>(0);
|
|
const onLayout = (e: LayoutChangeEvent) => {
|
|
const w = e.nativeEvent.layout.width || 0;
|
|
// 仅在宽度发生明显变化时才更新,避免渲染-布局循环
|
|
const next = Math.round(w);
|
|
if (next > 0 && next !== lastWidthRef.current) {
|
|
lastWidthRef.current = next;
|
|
setTrackWidth(next);
|
|
}
|
|
};
|
|
|
|
const fillWidth = Animated.multiply(animatedValue, trackWidth || 1);
|
|
const percent = Math.round(clamped * 100);
|
|
|
|
return (
|
|
<View style={[styles.container, { height }, style]} onLayout={onLayout}>
|
|
<View style={[styles.track, { backgroundColor: trackColor }]} />
|
|
<Animated.View style={[styles.fill, { width: fillWidth, backgroundColor: fillColor }]}>
|
|
{showLabel && (
|
|
<Text style={styles.label}>{percent}%</Text>
|
|
)}
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export const ProgressBar = memo(ProgressBarImpl, (prev, next) => {
|
|
// 仅在这些关键属性变化时重新渲染,忽略 style 的引用变化
|
|
return (
|
|
prev.progress === next.progress &&
|
|
prev.height === next.height &&
|
|
prev.trackColor === next.trackColor &&
|
|
prev.fillColor === next.fillColor &&
|
|
prev.animated === next.animated &&
|
|
prev.showLabel === next.showLabel
|
|
);
|
|
});
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
width: '100%',
|
|
borderRadius: 10,
|
|
overflow: 'hidden',
|
|
backgroundColor: 'transparent',
|
|
},
|
|
track: {
|
|
...StyleSheet.absoluteFillObject,
|
|
borderRadius: 10,
|
|
},
|
|
fill: {
|
|
height: '100%',
|
|
borderRadius: 10,
|
|
justifyContent: 'center',
|
|
paddingHorizontal: 10,
|
|
},
|
|
label: {
|
|
color: '#192126',
|
|
fontWeight: '700',
|
|
fontSize: 12,
|
|
},
|
|
});
|
|
|
|
|