乐趣区

关于javascript:wrnecharts在-RN-中使用-ECharts

本篇文章次要介绍 wrn-echarts 我的项目,接下来本文会从诞生背景、设计与实现、成果演示等环节向大家具体介绍这个我的项目。

官网:https://wuba.github.io/wrn-ec…
源码:https://github.com/wuba/wrn-e…

背景

在日常进行业务需要的开发时,常常会遇到须要绘制图表的场景,其中咱们应用频率最高的图表库是 ECharts。ECharts 作为市面上最成熟的图表库之一,次要面向 Web 端应用,官网对小程序端也提供了解决方案,而在 RN 的开发场景中却没有比拟好的实现办法,面对这种状况以前咱们的解决方案有:

  1. 放弃 ECharts,应用针对 RN 原生开发的图表库,如 react-native-charts-wrapper、victory-native 等
  2. 通过 Webview 来应用 web 端的 ECahrts,如 react-native-echarts-pro、native-echarts 等

计划 1,RN 现有图表库的款式与交互与 ECahrts 相比有较大差距,图表的丰富性也有余,尤其是有多端需要的场景下,须要为 RN 独自进行 UI 交互设计,设计与实现老本高。

计划 2,通过 Webview 的形式,当页面上有多个图表或者图表元素过多时,会遇到性能方面的瓶颈,比方安卓端的大数据量面积图、单轴散点图等会有白屏景象,而且失常渲染过程中也会有比拟显著的卡顿、掉帧的状况。

所以,咱们心愿开发一个图表库,能够应用 RN 的原生渲染控件将 Web ECharts 的能力集成到 RN 利用中,来进步开发效率以及不同平台产品体验的一致性,同时为咱们未来实现真正的跨端图表库打下基础。

可行性剖析

既然要用 RN 原生渲染,那首先看目前 RN 反对的图形库有哪些:

  • react-native-svg:提供可在 RN 应用的根底 SVG 图形库,该库通过相似于 Web 端的 SVG 渲染模式来渲染图形(此计划下文中简称 WRNSVG 模式)
  • react-native-skia:Skia 是跨平台的图形渲染引擎,该库将 Skia 的 2d 图形库引入 RN,同时也提供了 ImageSVG 组件,反对对 SVG 格局的图片进行渲染(此计划下文中简称 WRNSkia 模式)

剖析得之,上述两种计划实现的外围都是要获取到 ECharts 图表的 SVG 图形数据。而咱们晓得 ECharts 自身就反对 SVG 格局的渲染,所以咱们就去 ECharts 代码仓库查看相干的实现,看是否能获取到咱们想要的数据。

查看源码时咱们发现这部分性能是调用了 zrender 库的 SVGPainter 组件来实现,而且咱们能够通过革新该渲染组件来获取图表的 SVG 图形数据,所以咱们这个计划是可行的。并且因为 WRNSVG 模式和 WRNSkia 模式这两种计划外围流程类似,所以咱们打算同时反对这两种实现形式,让用户能够本人抉择适合本人的一种。

原理与实现

1. 架构设计

2. 外围流程

以 WRNSVG 模式为例,外围工作流程为:

  1. 替换 ECharts 的 SVGRenderer,将注册的 SVGPainter 替换为自定义的 CustomSVGPainter
  2. CustomSVGPainter 继承自 SVGPainter,重写了构造函数与 refresh 函数中的局部实现,当图表数据初始化或者更新时,调用 SVGComponent 上注册的 patch 函数,并把计算出的新的 SVG 数据传递过来
  3. 定义 SVGComponent,该组件治理以后图表实例,上有外围的 patch 函数,用来接管实时 SVG 数据,而后调用 SVGElement 函数
  4. SVGElement 函数遍历 SVG 所有节点,并转化为 react-native-svg 提供的相应 SVG 元素进行最终的渲染动作

与 WRNSkia 模式区别:

WRNSkia 模式绝对 WRNSVG 模式来说整体流程比较简单,定义的 SkiaComponent 组件上有一个外围办法 patchString,patchString 接管变动后的 SVG 数据,合并转化为 SVG 图片格式数据,传递给给 react-native-skia 的 ImageSVG 组件进行整体渲染即可

3. 解决 TouchEvent(手势事件)

Web ECharts 的事件是鼠标事件,例如 click、dblclick、mousedown、mousemove 等,通过鼠标事件来触发图表的元素显示或者动画。RN ECharts 须要把挪动端的 TouchEvent 模仿为鼠标事件,并派发到 ECharts init 办法生成的图表实例上。

比方图表上追随鼠标显示图例的动作,对应到挪动端就是 TouchStart + TouchMove,对应转化为鼠标事件是 mousedown + mousemove。还有比方图表的缩放,挪动端是双指按下缩放,对应则转化为鼠标的 mousewheel 事件,并且通过双指间隔变动来计算出对应的 mousewheel 滚动间隔。

要害代码:

  1. TouchEvent 转化为 MouseEvent

    // ...
    PanResponder.create({onPanResponderGrant: ({ nativeEvent}) => {
     // 动作开始,在这里转化为鼠标的点击与挪动事件
     dispatchEvent(
       zrenderId,
       ['mousedown', 'mousemove'],
       nativeEvent,
       'start'
     );
      },
      onPanResponderMove: ({nativeEvent}) => {
     // 解决手指挪动操作
     const length = nativeEvent.touches.length;
     if (length === 1) {// 在这里解决单指挪动操作...} else if (length === 2) {
       // 在这里解决双指挪动操作...
       if (!zooming) {// ...} else {
         // 在这里转化为滚轮的事件
         const {initialX, initialY, prevDistance} = pan.current;
         const delta = distance - prevDistance;
         pan.current.prevDistance = distance;
         dispatchEvent(zrenderId, ['mousewheel'], nativeEvent, undefined, {
           zrX: initialX,
           zrY: initialY,
           zrDelta: delta / 120,
         });
       }
     }
      },
      onPanResponderRelease: ({nativeEvent}) => {// 动作完结,在这里转化为鼠标点击开释操作...},
    }),
  2. 将 MouseEvent 利用到 ECharts 图表实例上

    function dispatchEvent(
      zrenderId: number,
      types: HandlerName[],
      nativeEvent: NativeTouchEvent,
      stage: 'start' | 'end' | 'change' | undefined,
      props: any = {
     zrX: nativeEvent.locationX,
     zrY: nativeEvent.locationY,
      }
    ) {if (zrenderId) {var handler = getInstance(zrenderId).handler;
     types.forEach(function (type) {
       handler.dispatch(type, {
         preventDefault: noop,
         stopImmediatePropagation: noop,
         stopPropagation: noop,
         ...props,
       });
       stage && handler.processGesture(wrapTouch(nativeEvent), stage);
     });
      }
    }

4. 问题与解决

当下面流程开发实现后,咱们先是用根底的简略图表进行测试,随后开始对 ECharts 的各种图表类型进行比照测试,测试过程中咱们发现并解决了很多图表显示中的异样,例如:

4.1 WRNSkia 模式下中文乱码问题

咱们发现应用 WRNSkia 模式渲染时,图表中的中文显示乱码。为什么会乱码?首先咱们猜想字体出了问题,查看字体文件信息,并没有中文字体,查阅了 react-native-skia,起因是不反对字体回退(字体回退:字符串中的某些字符在以后字体中不受反对时,会在字体队列中回退检索反对的字体)。

所以咱们找了个中文字体文件 Skia.Typeface.MakeFreeTypeFaceFromData,在运行时进行导入,这个时候能够失常显示了。然而此时,咱们又思考到在不同零碎上,应用字体是不一样的,一个中文字体文件很大,咱们也不可能每种都导入,然而只提供一种字体的话体验必定是不敌对的,那咱们能不能间接应用零碎自带的中文字体呢?

于是咱们对 iOS Android 反对的中文字体进行了调研,最初咱们决定了在有中文的文本设置时,Android 设置 font-family 应用 Noto Sans,iOS 应用 PingFang SC。

4.2 字体重叠和留白景象

中文乱码问题解决好后咱们又发现英文有字体重叠问题,咱们猜想是字体宽度计算的有问题,因为中文是等宽字体,所以没有问题。

查阅了 zrender 的 measureText 实现,其中在 svgrender 写死了字体为 sans-serif,依据此字体计算了一个宽度,如果是其余字体宽度超出,则会呈现重叠,如果设置的字体宽度小,又会呈现空白。

所以咱们将这部分改为应用 Noto Sans 和 PingFang SC 两种字体来计算宽度,遂解决。

我的项目中如何应用

在理论利用中,wrn-echarts 的整体流程与 ECharts 类似:

  1. 依据所应用渲染形式,抉择装置 react-native-svg 或 @shopify/react-native-skia
  2. 装置 wrn-echarts 并引入相干组件
  3. 应用 wrn-echarts 的 SVGRenderer 替换掉 ECharts 的 SVGRenderer
  4. 编写图表的 option 配置信息
  5. 应用 SkiaChart / SvgChart 组件

示例

import * as echarts from 'echarts/core';
import {useEffect, useRef} from 'react';
import {SVGRenderer, SvgChart} from 'wrn-echarts';

// 注册必须的组件
echarts.use([SVGRenderer, // 此处 SVGRenderer 为 wrn-echarts 的 SVGRenderer]);

export default function EchartsPage() {
  // svgRef 用来保留图表实例
  const svgRef = useRef<any>(null);
  // 编写图表配置文件(示例)const option = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {type: 'value'},
    series: [
      {data: [150, 230, 224, 218, 135, 147, 260],
        type: 'line'
      }
    ]
  };

  useEffect(() => {
    let chart;
    if (svgRef.current) {
      // 图表初始化
      chart = echarts.init(svgRef.current, 'light', {
        renderer: 'svg',
        width: 300,
        height: 300
      });
      chart.setOption(option);
    }
    return () => chart?.dispose();
  }, []);

  return <SvgChart ref={svgRef}></SvgChart>;
}

运行成果

真机演示

过来一段时间,咱们进行了大量的测试,尝试了各种不同类型的图表,也修复了很多存在的问题,目前 ECharts 反对的图表咱们绝大部分都曾经反对。

上面我会展现一部分真机的效果图,更多的示例能够在 https://github.com/wuba/taro-… 我的项目上查看

示例

   

计划比照

之前在背景中有提到过,咱们指标的场景是在 RN 端应用 ECharts,目前支流的计划均为通过 WebView 来实现,而泛滥基于 WebView 的实现中,react-native-echarts-pro 的使用者较多,所以咱们抉择了 react-native-echarts-pro 做为对照来进行比照测试。

工具应用“火山引擎”进行性能测评,以华为 nova5Pro 作为测试机,测试时别离用 svg、skia、react-native-echarts-pro 绘制同一图表,取图表从初始化到渲染实现时间段内的各项性能数据平均值,每种采样 20 次;性能指标蕴含整机 CPU 使用率、App CPU 使用率、FPS 均值(去除零值)、JavaHeap、NativeHeap、Code、Graphics、Other、System、卡顿数、卡登时长占比等。

局部外围指标比照数据如下:

1. 不同类型内存占用比照

单表大数据量的状况:

多图表同时渲染的状况:

比照后果:

JAVA 模块:在单表大数据量渲染时 WRNSVG 模式整体优于其余两种。而在进行多表同时渲染时,WRNSkia 与 WRNSVG 两种渲染模式都显著优于 react-native-echarts-pro 计划。

Native 模块:该项数据在不同场景下结论统一,WRNSVG 模式略优于其余两种计划。

图形渲染模块:在多图表同时渲染时,WRNSkia 与 WRNSVG 两种渲染模式整体都优于 react-native-echarts-pro 计划。

2. 整机 CPU 使用率比照

单表大数据量的状况:

多图表同时渲染的状况:

比照后果:该项比照数据表明,不同状况 WRNSkia 与 WRNSVG 两种模式在整机 CPU 使用率方面略优于 react-native-echarts-pro 计划。

3. FPS 均值比照(值越高越好)

单表大数据量的状况:

多图表同时渲染的状况:

比照后果:该项比照数据表明,在画面刷新频率上,react-native-echarts-pro 计划比较稳定,整体略优于本计划提供的两种模式,其中 WRNSVG 模式帧率绝对 WRNSkia 模式更加不稳固。

4. 其余

除以上列举比照数据之外,在图表初始化过程中,react-native-echarts-pro 计划整体画面的渲染速度较慢,看上面的动图能够直观的感触到这一差别:

5. 比照总结

下表为各项比照数据整顿,以 react-native-echarts-pro 计划为基准 3 分,依照 1 – 5 分来评估各种形式在不同指标下的体现,分值越高体现越好:

从表格比照可知,WRNSVG 模式整体体现最佳,WRNSkia 模式次之,这两种计划在内存相干的各项指标都不同水平的优于 react-native-echarts-pro 计划。只有在 FPS 指标中绝对于 react-native-echarts-pro 计划有肯定劣势,在应用中请结合实际状况抉择适宜本人的计划与渲染模式。

总结与布局

下面就是咱们对于 wrn-echarts 从打算、设计到实现的大略的流程。

其实这个过程并不是一帆风顺,比方咱们最开始是想在 RN 上实现跟 web 端 canvas 完全一致的 canvas2d 上下文,可是试验下来咱们发现这种形式实现起来过于简单,尽管咱们曾经能做到简略的图形绘制,但只是其中一部分的 API 的实现就消耗了咱们大量的精力,所以咱们放弃了这个方向并从新开始思考。

最终咱们找到了当初这种形式,绕过了底层接口的实现,转而作为一个中间人的角色,充分利用现有的轮子,提供了当初这种全新的在 RN 上应用 ECharts 的形式。

后续布局

  1. 实现 WRNSkia 的 Canvas 渲染模式:
    react-native-skia 除了提供 ImageSVG 组件,还提供了可在 RN 中应用的 Canvas 根底图形组件,因为现有的 WRNSkia 模式和 WRNSVG 模式在面对大量的数据处理时,内存占用和渲染效率等方面体现不是很好,所以咱们期待实现 WRNSkia 的 Canvas 渲染模式后在这些方面的体现会有晋升。
  2. 封装 taro-echarts:
    将 wrn-echarts 与其余各端的图表实现计划整合,封装为 taro-echarts 库,让 Taro 在跨端开发中能够实现图表相干需要(Taro:开源跨端跨框架解决方案,反对应用前端脚本语法来开发 Web、小程序、原生等利用,实现一套代码多端运行)。

wrn-echarts 我的项目现已开源,我的项目地址 https://github.com/wuba/wrn-e…,欢送 star。更多内容可在官网查看 https://wuba.github.io/wrn-ec…。

另外本篇文章中的示例代码在 https://github.com/wuba/taro-… 我的项目上,该仓库也是开源的,感兴趣的同学也能够间接从利用商店装置新版本的 Taro Playground 利用进行体验。

应用过程中,如遇任何问题,可增加“58 技术小秘书”或“Taro 小助手”为好友,备注“Taro RN”,退出官网交换群寻求帮忙。

 

作者简介

李志豪:58 同城 资深前端开发工程师

鸣谢

陈志庆:方案设计与外围模块开发

杨杨:提供测试用例以及测试数据

王信健:图表显示问题修复

袁津津:图表 Demo 整顿

退出移动版