feat(workout): 优化心率图表性能并移除每日总结通知功能
- 重构心率数据采样算法,采用智能采样保留峰值、谷值和变化率大的点 - 减少心率图表最大数据点数和查询限制,提升渲染性能 - 移除图表背景线样式,简化视觉呈现 - 完全移除每日总结通知功能相关代码和调用
This commit is contained in:
@@ -46,7 +46,7 @@ interface WorkoutDetailModalProps {
|
||||
const SCREEN_HEIGHT = Dimensions.get('window').height;
|
||||
const SHEET_MAX_HEIGHT = SCREEN_HEIGHT * 0.9;
|
||||
|
||||
const HEART_RATE_CHART_MAX_POINTS = 120;
|
||||
const HEART_RATE_CHART_MAX_POINTS = 80;
|
||||
|
||||
export function WorkoutDetailModal({
|
||||
visible,
|
||||
@@ -338,7 +338,7 @@ export function WorkoutDetailModal({
|
||||
height={220}
|
||||
fromZero={false}
|
||||
yAxisSuffix="次/分"
|
||||
withInnerLines
|
||||
withInnerLines={false}
|
||||
bezier
|
||||
chartConfig={{
|
||||
backgroundColor: '#FFFFFF',
|
||||
@@ -352,11 +352,6 @@ export function WorkoutDetailModal({
|
||||
strokeWidth: '2',
|
||||
stroke: '#FFFFFF',
|
||||
},
|
||||
propsForBackgroundLines: {
|
||||
strokeDasharray: '3,3',
|
||||
stroke: '#E3E6F4',
|
||||
strokeWidth: 1,
|
||||
},
|
||||
fillShadowGradientFromOpacity: 0.1,
|
||||
fillShadowGradientToOpacity: 0.02,
|
||||
}}
|
||||
@@ -475,14 +470,88 @@ function trimHeartRateSeries(series: WorkoutDetailMetrics['heartRateSeries']) {
|
||||
return series;
|
||||
}
|
||||
|
||||
const step = Math.ceil(series.length / HEART_RATE_CHART_MAX_POINTS);
|
||||
const reduced = series.filter((_, index) => index % step === 0);
|
||||
// 智能采样算法:保留重要特征点(峰值、谷值、变化率大的点)
|
||||
const result: typeof series = [];
|
||||
const n = series.length;
|
||||
|
||||
if (reduced[reduced.length - 1] !== series[series.length - 1]) {
|
||||
reduced.push(series[series.length - 1]);
|
||||
// 总是保留第一个和最后一个点
|
||||
result.push(series[0]);
|
||||
|
||||
// 计算心率变化率
|
||||
const changeRates: number[] = [];
|
||||
for (let i = 1; i < n; i++) {
|
||||
const prevValue = series[i - 1].value;
|
||||
const currValue = series[i].value;
|
||||
const prevTime = dayjs(series[i - 1].timestamp).valueOf();
|
||||
const currTime = dayjs(series[i].timestamp).valueOf();
|
||||
const timeDiff = Math.max(currTime - prevTime, 1000); // 至少1秒,避免除零
|
||||
const valueDiff = Math.abs(currValue - prevValue);
|
||||
changeRates.push(valueDiff / timeDiff * 1000); // 变化率:每秒变化量
|
||||
}
|
||||
|
||||
return reduced;
|
||||
// 计算变化率的阈值(前75%的分位数)
|
||||
const sortedRates = [...changeRates].sort((a, b) => a - b);
|
||||
const thresholdIndex = Math.floor(sortedRates.length * 0.75);
|
||||
const changeThreshold = sortedRates[thresholdIndex] || 0;
|
||||
|
||||
// 识别局部极值点
|
||||
const isLocalExtremum = (index: number): boolean => {
|
||||
if (index === 0 || index === n - 1) return false;
|
||||
|
||||
const prev = series[index - 1].value;
|
||||
const curr = series[index].value;
|
||||
const next = series[index + 1].value;
|
||||
|
||||
// 局部最大值
|
||||
if (curr > prev && curr > next) return true;
|
||||
// 局部最小值
|
||||
if (curr < prev && curr < next) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 遍历所有点,选择重要点
|
||||
let minDistance = Math.max(1, Math.floor(n / HEART_RATE_CHART_MAX_POINTS));
|
||||
|
||||
for (let i = 1; i < n - 1; i++) {
|
||||
const shouldKeep =
|
||||
// 1. 是局部极值点
|
||||
isLocalExtremum(i) ||
|
||||
// 2. 变化率超过阈值
|
||||
(i > 0 && changeRates[i - 1] > changeThreshold) ||
|
||||
// 3. 均匀分布的点(确保基本覆盖)
|
||||
(i % minDistance === 0);
|
||||
|
||||
if (shouldKeep) {
|
||||
// 检查与上一个选中点的距离,避免过于密集
|
||||
const lastSelectedIndex = result.length > 0 ?
|
||||
series.findIndex(p => p.timestamp === result[result.length - 1].timestamp) : 0;
|
||||
|
||||
if (i - lastSelectedIndex >= minDistance || isLocalExtremum(i)) {
|
||||
result.push(series[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保最后一个点被包含
|
||||
if (result[result.length - 1].timestamp !== series[n - 1].timestamp) {
|
||||
result.push(series[n - 1]);
|
||||
}
|
||||
|
||||
// 如果结果仍然太多,进行二次采样
|
||||
if (result.length > HEART_RATE_CHART_MAX_POINTS) {
|
||||
const finalStep = Math.ceil(result.length / HEART_RATE_CHART_MAX_POINTS);
|
||||
const finalResult = result.filter((_, index) => index % finalStep === 0);
|
||||
|
||||
// 确保最后一个点被包含
|
||||
if (finalResult[finalResult.length - 1] !== result[result.length - 1]) {
|
||||
finalResult.push(result[result.length - 1]);
|
||||
}
|
||||
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderHeartRateZone(zone: HeartRateZoneStat) {
|
||||
@@ -745,6 +814,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
chartStyle: {
|
||||
marginLeft: -10,
|
||||
marginRight: -10,
|
||||
},
|
||||
chartEmpty: {
|
||||
paddingVertical: 32,
|
||||
|
||||
Reference in New Issue
Block a user