React-Native开发鸿蒙NEXT-svg绘制睡眠质量图part2
FBI WARNING:
The complete demo will be posted at the end of the series, so no need to worry.

上回进展到在画布上画出了睡眠采样点的数据,下面要做的是把同一阶段的相邻的点连接起来,让它具有形状。
先做个简单版的,类似excel那样用个方框连一连。
在svg中,可以用Rect来绘制一个矩形。基本的语法如下
typescript
<Rect
key={`connector-${areaData.areaIndex}`}
x={areaData.aeraX}
y={areaData.aeraY}
width={areaData.areaWidth}
height={areaData.areaHeight}
/>,
知道起点坐标加上宽高即可。首先创建一个areaData数组,用于记录矩形的绘制矩形的基本信息(暂时只要关注areaIndex,areaX,areaY,areaWidth,areaHeight)
typescript
interface SleepAreaData {
areaIndex: number; // 区域索引
aeraX: number; // 区域X坐标
aeraY: number; // 区域Y坐标
areaWidth: number; // 区域宽度
areaHeight: number; // 区域宽度
areaColor: any | null; // 区域颜色
areaStoke: any | null; // 区域阴影
areaLeftUpDown: UpDownEnum; // 区域左边上升下降趋势
areaRightUpDown: UpDownEnum; // 区域左边上升下降趋势
areaStage: number; // 睡眠阶段
areaBeginIndex: number; // 区域开始索引
areaEndIndex: number; // 区域结束索引
}
接着开始遍历画布上的数据点集合points,循环计算用于构建一个SleepAreaData数组,每计算出一个SleepAreaData对象,同时创建一个Rect对象。
typescript
const calcPointData = () => {
let areaData = initAreaData();
let svgsTemp: JSX.Element[] = [];
points.map((point, index) => {
if (index == 0) {
areaData.areaIndex = index;
areaData.aeraX = point.x;
areaData.aeraY = point.y;
areaData.areaWidth = 0;
areaData.areaColor = `url(#gradient${point.stage})`;
areaData.areaStoke = STAGE_CONFIG[point.stage].shadow;
areaData.areaLeftUpDown = UpDownEnum.UP;
areaData.areaStage = point.stage;
areaData.areaBeginIndex = index;
}
if (index - 1 >= 0) {
const prevPoint = points[index - 1];
const isSameStage = point.stage === prevPoint.stage;
areaData.areaWidth += point.x - prevPoint.x;
let deltaY1 = 0;
let deltaY2 = 0;
if (!isSameStage) {
if (point.stage > prevPoint.stage) {
deltaY1 = -AREA_HEIGHT;
} else {
deltaY2 = -AREA_HEIGHT;
}
areaData.areaEndIndex = index;
console.log('areaData.areaEndIndex = ' + index);
areaDataList.push(areaData);
// 输出
svgsTemp.push(
<Rect
key={`connector-${areaData.areaIndex}`}
x={areaData.aeraX}
y={areaData.aeraY}
width={areaData.areaWidth}
height={areaData.areaHeight}
/>,
);
// 重新开始绘制矩形
areaData = initAreaData();
areaData.areaIndex = index;
areaData.aeraX = point.x;
areaData.aeraY = point.y;
areaData.areaWidth = 0;
areaData.areaColor = STAGE_CONFIG[point.stage].color;
areaData.areaStoke = STAGE_CONFIG[point.stage].shadow;
areaData.areaStage = point.stage;
areaData.areaBeginIndex = index;
if (point.stage > prevPoint.stage) {
areaData.areaLeftUpDown = UpDownEnum.UP;
} else {
areaData.areaLeftUpDown = UpDownEnum.DOWN;
}
}
}
if (index == points.length - 1) {
areaData.areaRightUpDown = UpDownEnum.DOWN;
areaData.areaEndIndex = index;
// 输出
areaDataList.push(areaData);
svgsTemp.push(
<Rect
key={`connector-${areaData.areaIndex}`}
x={areaData.aeraX}
y={areaData.aeraY}
width={areaData.areaWidth}
height={areaData.areaHeight}
/>,
);
}
});
return svgsTemp;
};
最终,这个方法向svg画布直接输出了一个Rect对象数组。
typescript
{/* 可视化图表 */}
<Svg height={CHART_HEIGHT + 80} width={SCREEN_WIDTH}>
......
{/* 绘制 */}
{data.length > 1 && calcPointData()}
......
</Svg>
可以看到,相邻的阶段被用矩形连接了起来。

通过同时显示矩形和点,可以很直观地感受到上回说到的一个最重要的逻辑:

下一步的想法就是给这些方块做下圆角,上点颜色,再看看是否能做个渐变色,这样至少颜色上可以做到竞品的效果。这些Rect都支持
typescript
<Rect
key={`connector-${areaData.areaIndex}`}
x={areaData.aeraX}
y={areaData.aeraY}
width={areaData.areaWidth}
height={areaData.areaHeight}
stroke={areaData.areaStoke}
fill={areaData.areaColor}
rx={5}
ry={5}
/>,

把上下相邻的两个阶段方块间加上虚线连接,线段的设置逻辑同样是在calcPointData中
typescript
......
// 出一条线
if (index != data.length - 1) {
svgsTemp.push(
<Line
key={`line-${index}`}
x1={point.x}
y1={point.y - deltaY1}
x2={point.x}
y2={areaData.aeraY - deltaY2}
stroke="#999"
strokeWidth="1"
strokeDasharray="4 2"
/>,
);
}
// 重新开始绘制矩形
areaData = initAreaData();
areaData.areaIndex = index;
areaData.aeraX = point.x;
areaData.aeraY = point.y;
areaData.areaWidth = 0;
加上虚线连接,关掉数据点的显示后,此时的效果和网上为数不多的几个绘制睡眠图的开源库已经较为接近了

至此还剩下最后一步---把图上的矩形换成自定义的图形,根据不同阶段使用不同的图形样式。
To Be Continued...
不经常在线,有问题可在微信公众号或者掘金社区私信留言
更多内容可关注
我的公众号悬空八只脚