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


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

更多内容可关注

我的公众号悬空八只脚

相关推荐
想你依然心痛10 分钟前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“数智视界“——PC端AI智能体沉浸式数据可视化分析工作台
华为·ar·harmonyos·智能体
前端不太难8 小时前
从单页面到系统化:鸿蒙 App 演进路径
华为·状态模式·harmonyos
想你依然心痛9 小时前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“文思智脑“——PC端AI智能体沉浸式智能写作工作台
人工智能·ar·harmonyos·ai写作
小雨青年9 小时前
鸿蒙 HarmonyOS 6 | Pura X Max 鸿蒙原生适配 09:展开态列表增加字段但不变复杂
华为·harmonyos
richard_yuu10 小时前
鸿蒙治愈游戏模块实战|四大轻量解压游戏、ArkTS动画交互与低功耗落地
游戏·交互·harmonyos
阿钱真强道14 小时前
24 鸿蒙LiteOS GPIO中断实战:从原理到上升沿/下降沿详解
harmonyos·中断·rk·liteos·开源鸿蒙·瑞芯微·rk2206
cd_9492172116 小时前
鸿蒙系统下抖音存储空间不足怎么办?缓存清理教程
缓存·华为·harmonyos
轻口味18 小时前
HarmonyOS 6.1 全栈实战录 - 14 渲染树透镜:FrameNode 渲染状态感知与高性能 UI 调优实战
ui·华为·harmonyos
HwJack2018 小时前
HarmonyOS NEXT 游戏APP开发中如何正确拦截退出手势
游戏·华为·harmonyos
HwJack2019 小时前
HarmonyOS APP开发中ArkTS/JS 类型错误全景拆解
javascript·华为·harmonyos