React-Native开发鸿蒙NEXT-svg绘制睡眠质量图part2

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...


不经常在线,有问题可在微信公众号或者掘金社区私信留言

更多内容可关注

我的公众号悬空八只脚

相关推荐
zhanshuo4 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo4 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos
whysqwhw9 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw11 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw12 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw13 小时前
鸿蒙音频编码
harmonyos
whysqwhw13 小时前
鸿蒙音频解码
harmonyos
whysqwhw13 小时前
鸿蒙视频解码
harmonyos
whysqwhw14 小时前
鸿蒙视频编码
harmonyos
ajassi200014 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos