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


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

更多内容可关注

我的公众号悬空八只脚

相关推荐
幽蓝计划3 小时前
鸿蒙跨平台开发教程之Uniapp布局基础
harmonyos
周胡杰4 小时前
鸿蒙接入flutter环境变量配置windows-命令行或者手动配置-到项目的创建-运行demo项目
javascript·windows·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
半青年5 小时前
华为鸿蒙电脑能否作为开发机?开发非鸿蒙应用?
ide·华为·编辑器·电脑·idea·harmonyos·visual studio
bestadc8 小时前
鸿蒙 核心与非核心装饰器
harmonyos
@兔然暴富@8 小时前
#跟着若城学鸿蒙# HarmonyOS NEXT学习之AlphabetIndexer组件详解
harmonyos
沙振宇11 小时前
【HarmonyOS】ArkTS开发应用的横竖屏切换
android·华为·harmonyos
bestadc12 小时前
鸿蒙 从打开一个新窗口到Stage模型的UIAbility组件
harmonyos
雪芽蓝域zzs17 小时前
鸿蒙Next开发 获取APP缓存大小和清除缓存
缓存·华为·harmonyos
鸿蒙布道师21 小时前
鸿蒙NEXT开发动画案例5
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
康康这名还挺多1 天前
鸿蒙HarmonyOS list优化一: list 结合 lazyforeach用法
数据结构·list·harmonyos·lazyforeach