如何在一台普通笔记本上保持流畅。
交易界面很重:漫长的历史、永不停歇的行情、一个页面上的多张图表。TradeCanvas 每一帧只做必要的工作,在主线程上完成,不含任何第三方代码。本页说明它是怎么做到的,以及还在哪些地方改进。
仍在进行中:引擎已经快到可以投入使用,但还没有达到它将来的速度。最后一节列出了我们已知尚待完成的部分。
小巧的包,单向依赖
每个包只依赖它下面的包。你的页面里不会运行 TradeCanvas 之外的任何代码:框架组件把 React、Vue 或 Svelte 作为 peer 依赖,WebGL 渲染器是一个独立的代码块,只在图表需要时才加载。
0 个第三方运行时依赖
一笔行情的路径
价格从你的数据源到达,在一帧之内呈现在屏幕上。每个阶段只处理发生变化的部分。
- 1你的数据源
DataAdapterWebSocket、REST、经纪商:任何提供历史数据和数据流的来源。 - 2流管理器
StreamManager先加载历史,再接入实时。过时请求的响应会被丢弃;数据源沉默超过 30 秒即可察觉。 - 3行情聚合器
TickAggregator每笔行情进入容量为 1,000 的环形缓冲区,并合并到所属周期的K线中。 - 4指标
IndicatorEngine从最后一根K线重新计算,而不是从序列开头。 - 5视口
Viewport只遍历屏幕上的K线;价格刻度也只由它们决定。 - 6渲染循环
RenderLoop无论到达多少笔行情,每个动画帧最多绘制一次场景。 - 7Canvas 2D 或 WebGL
RenderEngine两层画布;开启 WebGL 时,下面还有一层 GPU 画布。
一笔行情只花一根K线的代价,而不是整段历史
当一笔行情改变了最后一根K线时,每个指标都会从这根K线(以及其公式需要回看的前几根)继续计算,而不是重算整个序列。如果它记住的K线已经对不上,就退回到完整重算,因此不会漏掉任何修正。
约 20 个指标有各自的增量更新,另有 31 个建立在逐点续算的基础之上。读取另一个指标线条的指标同样可以续算。
缩小时:每像素一列
当K线窄于一个像素时,逐根绘制只是在做肉眼看不见的工作。蜡烛图、美国线、高低线和成交量按每像素一列绘制,取该列下所有K线的第一个开盘价、最高的最高价、最低的最低价和最后一个收盘价。折线和面积图每像素保留约两个点(LTTB);指标线和通道每个像素列绘制一段。
- 视图中的K线
- 313
- 每像素K线数
- 0.39
- 绘制的图形
- 313
一根K线永远不会窄于四分之一个 CSS 像素,因此一张容纳百万根K线的图表在完全缩小时每像素最多显示约四根K线:在 1,600 px 宽的图表上约为 6,400 根。百万K线基准测试衡量的正是平移浏览其余部分的表现。
两层画布,悬停几乎没有代价
场景(网格、序列、指标、画线、订单)位于一层画布上;十字光标、图例、坐标轴标签和倒计时位于其上方一层轻薄的画布上。移动鼠标只重绘顶层。数据更新则在下一帧重绘一次场景。
每一层全尺寸画布每帧都要让浏览器做一次合成,因此引擎尽量少用:仅用于 2D 绘制的后台画布只在有需要时才存在。
WebGL 靠记录,而不是重写
图表通过 Canvas 2D 调用来绘制。开启 WebGL 后,这些调用会交给一个记录器,由它把路径、填充和描边转换为以设备像素为单位的 GPU 命令。GPU 无法画得一模一样的内容会按顺序留在 Canvas 2D 上,因此两种方式画出的结果相同。
- 路径、填充和描边
- 蜡烛、成交量和网格以实例化方式绘制
- 整个序列存放在一个 Float32Array 中,每根K线六个浮点数;一笔行情只重写末尾
- 文本
- 图像和图案
- 阴影、滤镜、特殊裁剪
- GPU 无法处理的第一步之后的所有内容
renderer: 'auto'会向浏览器请求一个没有重大性能缺陷的上下文,因此在 WebGL 只能以软件方式运行的机器上会保留 Canvas 2D。- 一个页面上最多 8 张图表持有 WebGL 上下文(
setMaxWebGLCharts);其余图表排队等待,并用 Canvas 2D 绘制。 - 如果 GPU 丢失了上下文,图表会改用 Canvas 2D 继续运行,并在上下文恢复后重新用 WebGL 构建。
- 蜡烛在着色器和 CPU 上按同一规则对齐到整数设备像素,因此两种渲染器会把它们画在相同的像素上。
为低配机器而设计
- 默认使用 Canvas 2D在更多设备上完成测量之前,WebGL 需要手动开启。在高像素比的集成显卡上,瓶颈是浏览器自身的合成,而不是绘制。
- 每帧只调整一次尺寸resize 事件会合并为每帧一次,尺寸没有变化时什么也不做。
- 分页加载历史向回滚动时,较早的K线每次加载 500 根,一次只发一个请求,失败后会退避。
- 没有加载闪烁加载遮罩会等待 200 ms,因此快速切换品种时不会闪烁。
- 下载体积小无界面图表 gzip 后为 176 KB,完整组件为 315 KB,使用 WebGL 渲染器时再加 18 KB。
- 原地刷新的报价板bo-grid 行情报价板把行情合并为每帧一次补丁,只重绘发生变化的单元格。