feat(miniprogram): TrendChart Canvas 适老化 — useCanvasTokens + 斜线纹理 + tooltip 常驻 (U2-1)

- 替换所有硬编码字号/颜色为 useCanvasTokens 动态 token
- 关怀模式 Y/X 轴字号 14px,异常点半径 8px,正常点半径 5px
- 参考区间带增加斜线纹理增强区分度(关怀模式)
- 关怀模式 tooltip 默认显示最后一个数据点(常驻)
- tooltip SCSS 适老:padding 12/16px,min-height 44px
This commit is contained in:
iven
2026-05-22 08:20:57 +08:00
parent b44ed6dfd2
commit 8d2c377b68
2 changed files with 60 additions and 10 deletions

View File

@@ -57,9 +57,21 @@
white-space: nowrap;
pointer-events: none;
z-index: 10;
.elder-mode & {
padding: 12px 16px;
border-radius: 8px;
min-height: 44px;
display: flex;
align-items: center;
}
}
.trend-tooltip-text {
font-size: 12px;
color: #fff;
.elder-mode & {
font-size: var(--tk-font-body-sm);
}
}

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useRef, useCallback, useState } from 'react';
import { Canvas, View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useCanvasTokens } from '@/hooks/useCanvasTokens';
import './index.scss';
/** Canvas 2D 上下文类型 — 微信小程序 Canvas 2d 接口 */
@@ -44,9 +45,28 @@ export default React.memo(function TrendChart({
unit = '',
height = 500,
}: TrendChartProps) {
const tokens = useCanvasTokens();
const canvasRef = useRef<any>(null);
const [tooltip, setTooltip] = useState<{ date: string; value: number; x: number } | null>(null);
// 关怀模式默认显示最后一个数据点 tooltip
useEffect(() => {
if (tokens.yLabelFontSize >= 14 && data && data.length > 0 && canvasRef.current) {
const dpr = getDPR();
const w = canvasRef.current.width / dpr;
const pad = { left: 45, right: 15 };
const cw = w - pad.left - pad.right;
const lastIdx = data.length - 1;
setTooltip({
date: data[lastIdx].date,
value: data[lastIdx].value,
x: pad.left + (lastIdx / Math.max(data.length - 1, 1)) * cw,
});
} else if (tokens.yLabelFontSize < 14) {
setTooltip(null);
}
}, [data, tokens.yLabelFontSize]);
const draw = useCallback(() => {
const node = canvasRef.current;
if (!node || !data || data.length === 0) return;
@@ -82,12 +102,29 @@ export default React.memo(function TrendChart({
if (referenceMin != null && referenceMax != null) {
const ry1 = toY(referenceMax);
const ry2 = toY(referenceMin);
ctx.fillStyle = 'rgba(5,150,105,0.08)';
ctx.fillStyle = tokens.referenceBandColor;
ctx.fillRect(pad.left, ry1, cw, ry2 - ry1);
// 关怀模式增加斜线纹理增强区分度
if (tokens.yLabelFontSize >= 14) {
ctx.save();
ctx.strokeStyle = 'rgba(5,150,105,0.18)';
ctx.lineWidth = 1;
const step = 8;
ctx.beginPath();
for (let x = pad.left; x < pad.left + cw; x += step) {
const x1 = Math.min(x, pad.left + cw);
const x2 = Math.min(x + (ry2 - ry1), pad.left + cw);
ctx.moveTo(x1, ry2);
ctx.lineTo(x2, ry1);
}
ctx.stroke();
ctx.restore();
}
}
// Grid lines
ctx.strokeStyle = '#F3F4F6';
ctx.strokeStyle = tokens.gridColor;
ctx.lineWidth = 1;
const gridLines = 4;
for (let i = 0; i <= gridLines; i++) {
@@ -99,8 +136,8 @@ export default React.memo(function TrendChart({
}
// Y-axis labels
ctx.fillStyle = '#94A3B8';
ctx.font = '10px sans-serif';
ctx.fillStyle = tokens.tx2;
ctx.font = `${tokens.yLabelFontSize}px sans-serif`;
ctx.textAlign = 'right';
for (let i = 0; i <= gridLines; i++) {
const val = yMax - (yTotal / gridLines) * i;
@@ -109,6 +146,7 @@ export default React.memo(function TrendChart({
}
// X-axis labels
ctx.font = `${tokens.xLabelFontSize}px sans-serif`;
ctx.textAlign = 'center';
const step = Math.max(1, Math.floor(data.length / 5));
for (let i = 0; i < data.length; i += step) {
@@ -130,13 +168,13 @@ export default React.memo(function TrendChart({
ctx.lineTo(chartPoints[chartPoints.length - 1].x, toY(yMin));
ctx.closePath();
const grad = ctx.createLinearGradient(0, pad.top, 0, pad.top + ch);
grad.addColorStop(0, 'rgba(8,145,178,0.15)');
grad.addColorStop(1, 'rgba(8,145,178,0.01)');
grad.addColorStop(0, tokens.areaGradientStart);
grad.addColorStop(1, tokens.areaGradientEnd);
ctx.fillStyle = grad;
ctx.fill();
// Line
ctx.strokeStyle = '#0891B2';
ctx.strokeStyle = tokens.lineColor;
ctx.lineWidth = 2;
drawLine(ctx, chartPoints);
@@ -147,13 +185,13 @@ export default React.memo(function TrendChart({
(referenceMin != null && d.value < referenceMin) ||
(referenceMax != null && d.value > referenceMax);
ctx.beginPath();
ctx.arc(chartPoints[i].x, chartPoints[i].y, isAbnormal ? 5 : 3, 0, Math.PI * 2);
ctx.fillStyle = isAbnormal ? '#DC2626' : '#0891B2';
ctx.arc(chartPoints[i].x, chartPoints[i].y, isAbnormal ? tokens.pointAbnormalRadius : tokens.pointNormalRadius, 0, Math.PI * 2);
ctx.fillStyle = isAbnormal ? tokens.abnormalColor : tokens.lineColor;
ctx.fill();
}
ctx.restore();
}, [data, referenceMin, referenceMax]);
}, [data, referenceMin, referenceMax, tokens]);
const handleTouchStart = useCallback((e: any) => {
if (!data || data.length === 0 || !canvasRef.current) return;