Skip to content

插件 ​

插件从外部扩展图表:在子图中绘制(序列下方或上方)、读取 K 线,其余一切都通过公开 API 完成,比如注册画线类型、监听事件、添加指标。可选功能也可以这样做成独立的包。

第一个插件 ​

这个插件标出可见 K 线中的最高价和最低价:

ts
import type { ChartPlugin } from '@kumochart-sdk/core';

const visibleExtremes: ChartPlugin = {
  name: 'visible-extremes',
  paint: ({ ctx, layer, pane, bars, visible, theme }) => {
    if (layer !== 'above' || pane.id !== 'main' || !visible) {
      return;
    }

    let high = -Infinity;
    let low = Infinity;

    for (let index = visible.from; index <= visible.to; index += 1) {
      high = Math.max(high, bars.high(index));
      low = Math.min(low, bars.low(index));
    }

    ctx.setLineDash([4, 4]);
    ctx.strokeStyle = theme.textMuted;
    ctx.fillStyle = theme.textMuted;

    for (const value of [high, low]) {
      const y = Math.round(pane.valueToY(value)) + 0.5;
      ctx.beginPath();
      ctx.moveTo(0, y);
      ctx.lineTo(pane.width, y);
      ctx.stroke();
      ctx.fillText(pane.formatValue(value), 8, y - 4);
    }
  }
};

chart.plugins.add(visibleExtremes);
chart.plugins.remove('visible-extremes');

每当图表重绘序列时都会调用 paint:数据变化、滚动、缩放、尺寸变化和修改配置之后。十字光标移动和拖动画线不会重绘序列,所以这些操作不会产生插件的开销。

绘制 ​

paint(context) 会对每个子图、每一层各调用一次:

  • below:在网格之后、序列之前。适合绘制背景类内容,例如成交量分布或交易时段底色。
  • above:在序列和指标之后,位于画线、委托单和十字光标之下。

上下文已经裁剪到子图的绘图区,并平移到它的左上角,单位是 CSS 像素;图表会在每个插件前后保存和恢复画布状态。

成员说明
ctx、pixelRatio2D 上下文和设备像素比,用来画出清晰的线
layerbelow 或 above
pane{ id, width, height, valueToY(value), yToValue(y), formatValue(value) }
bars、source显示的 K 线和源数据(见下文)
visible与绘图区相交的显示 K 线的序号范围 { from, to },没有时为 null
barSpacing相邻 K 线中心的间距(像素)
indexToX(index)、xToIndex(x)连续的 K 线序号与 x 坐标互相换算
timeToX(time)任意时间的 x 坐标,K 线之间插值,数据范围之外外推
theme、font主题和图表字体 { family, size }

withAlpha(color, alpha) 可以把主题色变成半透明的颜色。

读取数据 ​

bars 是显示的 K 线:按周期聚合、经过 K 线变换,并被正在进行的回放截断。source 是应用推送的源数据,按源周期组织,止于回放游标;它的粒度更细,比如日 K 里的每一根 1 分钟 K 线。没有设置源周期时,两者是同一组 K 线。

两者都是 BarsView:列式、只读。应用也可以通过 chart.data.bars 和 chart.data.source 拿到同样的视图:

成员说明
lengthK 线数量
revisionK 线每次变化都会改变
time(i)、open(i)、high(i)、low(i)、close(i)、volume(i)第 i 根 K 线的字段,不分配对象
bar(i)以对象形式返回这根 K 线,超出范围时为 null
lowerBound(time)第一个时间不早于 time 的序号;全部更早时为 length

用 revision 和读取的序号范围作为缓存键,插件就只在数据变化时计算一次,而不是每帧都算:

ts
let key = '';
let cached: Summary | undefined;

function summaryOf(source: BarsView, from: number, to: number): Summary {
  const next = `${source.revision}:${from}:${to}`;

  if (next !== key || !cached) {
    key = next;
    cached = summarize(source, from, to);
  }

  return cached;
}

显示 K 线 [from, to] 对应的源数据从 source.lowerBound(bars.time(from)) 开始,到 source.lowerBound(bars.time(to + 1)) 之前结束;最后一根显示 K 线则到 source.length 为止。

宿主 ​

attach(host) 在第一次绘制之前调用,传入图表和同样的数据视图:

成员说明
chart公开的 API:注册画线类型、订阅事件等
bars、source数据视图
repaint()在下一帧重绘,例如插件自身的设置改变之后

有设置项的插件可以保存宿主,在设置改变时请求重绘。下面这个水印插件在 K 线后面显示一段文字:

ts
import { withAlpha, type ChartPlugin, type PluginHost } from '@kumochart-sdk/core';

function watermark(initial: string): ChartPlugin & { setLabel(label: string): void } {
  let label = initial;
  let host: PluginHost | null = null;

  return {
    name: 'watermark',
    attach: (given) => {
      host = given;
    },
    detach: () => {
      host = null;
    },
    paint: ({ ctx, layer, pane, theme, font }) => {
      if (layer !== 'below' || pane.id !== 'main') {
        return;
      }

      ctx.font = `600 ${font.size * 4}px ${font.family}`;
      ctx.fillStyle = withAlpha(theme.textMuted, 0.15);
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(label, pane.width / 2, pane.height / 2);
    },
    setLabel: (next) => {
      label = next;
      host?.repaint();
    }
  };
}

const mark = watermark('BTC/USDT · 15m');
chart.plugins.add(mark);
mark.setLabel('ETH/USDT · 1h');

attach() 中建立的东西要在 detach() 中释放;chart.plugins.remove() 和 chart.destroy() 都会调用它,此时图表仍然可用:

ts
attach: (host) => {
  off = host.chart.on('replay', ({ state }) => updateLabel(state.time));
},
detach: () => {
  off?.();
}

读取数据的画线 ​

自定义画线类型的几何信息里也有同样的视图(geometry.bars、geometry.source),所以固定范围成交量分布、仓位这类测量行情的工具不需要插件也能实现。当然也可以由插件在 attach() 中注册它们。

发布插件 ​

  • 导出一个接收选项、返回插件的工厂函数,这样用户可以用不同的名称添加多个实例。
  • 把 @kumochart-sdk/core 声明为 peer dependency,版本与你开发时使用的一致。
  • 在创建插件时校验选项,并像图表一样抛出指明错误值的 ChartError。
  • 不要把状态放在插件对象之外:一个插件实例只属于一个图表。

基于 MIT 许可证发布。