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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user