KLineChart 使用教程

举报
Tony Ren 发表于 2026/10/27 07:10:55 2026/10/27
【摘要】 KLineChart 是一个基于 HTML5 Canvas 的轻量级金融 K 线图库:零第三方依赖,gzip 后仅约 40KB,内置多种技术指标和画线工具,支持移动端和 TypeScript,可用于任何前端框架(React / Vue / Angular / Svelte / 原生 JS 均可)。

KLineChart 使用教程

KLineChart 是一个基于 HTML5 Canvas 的轻量级金融 K 线图库:零第三方依赖,gzip 后仅约 40KB,内置多种技术指标和画线工具,支持移动端和 TypeScript,可用于任何前端框架(React / Vue / Angular / Svelte / 原生 JS 均可)。


目录

  1. 安装
  2. 创建第一个图表
  3. 数据接入:DataLoader
  4. 接入真实数据(REST + WebSocket)
  5. 技术指标
  6. 样式配置
  7. 画线与标注(Overlay)
  8. 常用 API 速查
  9. 常见问题(FAQ)

一、安装

包管理器(推荐)

npm install klinecharts
# 或
yarn add klinecharts
# 或
pnpm install klinecharts
# 或
bun add klinecharts

CDN(UMD 版,全局变量 klinecharts)

<script src="cdn.jsdelivr.net/npm/klinecharts/dist/umd/klinecharts.min.js"></script>

二、创建第一个图表

核心三步:init 初始化 → setSymbol 设置标的 → setPeriod 设置周期 → setDataLoader 喂数据。

原生 HTML

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="utf-8" />
  <script src="cdn.jsdelivr.net/npm/klinecharts/dist/umd/klinecharts.min.js"></script>
</head>
<body>
  <div id="chart" style="width:600px;height:600px"></div>
  <script>
    const chart = klinecharts.init('chart')
    chart.setSymbol({ ticker: 'TestSymbol' })
    chart.setPeriod({ span: 1, type: 'day' })
    chart.setDataLoader({
      getBars: ({ callback }) => {
        callback([
          { timestamp: 1517846400000, open: 7424.6, high: 7511.3, low: 6032.3, close: 7310.1, volume: 224461 },
          { timestamp: 1517932800000, open: 7310.1, high: 8499.9, low: 6810,   close: 8165.4, volume: 148807 },
          { timestamp: 1518019200000, open: 8166.7, high: 8700.8, low: 7400,   close: 8245.1, volume: 24467 },
          { timestamp: 1518105600000, open: 8244,   high: 8494,   low: 7760,   close: 8364,   volume: 29834 },
          { timestamp: 1518192000000, open: 8363.6, high: 9036.7, low: 8269.8, close: 8311.9, volume: 28203 }
        ])
      }
    })
  </script>
</body>
</html>

React

import { useEffect } from 'react'
import { init, dispose } from 'klinecharts'

export default () => {
  useEffect(() => {
    const chart = init('chart')
    chart.setSymbol({ ticker: 'TestSymbol' })
    chart.setPeriod({ span: 1, type: 'day' })
    chart.setDataLoader({
      getBars: ({ callback }) => { callback(/* KLineData 数组 */) }
    })
    return () => dispose('chart')   // 组件卸载时销毁图表
  }, [])
  return <div id="chart" style={{ width: 600, height: 600 }} />
}

Vue 3

<script setup>
import { onMounted, onUnmounted } from 'vue'
import { init, dispose } from 'klinecharts'

let chart
onMounted(() => {
  chart = init('chart')
  chart.setSymbol({ ticker: 'TestSymbol' })
  chart.setPeriod({ span: 1, type: 'day' })
  chart.setDataLoader({ getBars: ({ callback }) => { callback(/* ... */) } })
})
onUnmounted(() => dispose('chart'))
</script>

<template>
  <div id="chart" style="width:600px;height:600px" />
</template>

数据格式(KLineData):每根 K 线一个对象,timestamp 必须是毫秒:

interface KLineData {
  timestamp: number  // 毫秒时间戳
  open: number
  high: number
  low: number
  close: number
  volume: number
  turnover?: number  // 可选:成交额
}

三、数据接入:DataLoader

setDataLoader 是图表取数据的唯一入口,包含三个方法:

interface DataLoader {
  getBars: (params) => void | Promise<void>       // 拉取历史数据
  subscribeBar: (params) => void                  // 订阅实时数据
  unsubscribeBar: (params) => void                // 取消订阅
}

getBars:历史数据(支持分页)

chart.setDataLoader({
  getBars({ type, timestamp, symbol, period, callback }) {
    // type: 'init'    初始加载 / 切换品种周期后重新加载(timestamp 为 null)
    //       'forward'  向左滚动,加载更早的数据
    //       'backward' 向右滚动,加载更新的数据
    // timestamp: 分页锚点(forward 时通常为最左一根的时间戳)
    callback(bars, more)
  }
})

callback 的 more 参数:告诉图表某方向是否还有更多数据,用于继续触发分页:

callback(bars, { forward: true, backward: false })  // 还有更早数据
callback(bars, false)                                // 两个方向都没有了

合并逻辑由库自动处理:init 替换全部数据,forward 前插,backward 后插。

注意事项:

  • 返回前先按 timestamp 排序,避免重复时间戳;
  • 请求竞态时以当前品种/周期的最新请求为准。

subscribeBar:实时数据

初始 getBars 完成后调用,用于接收实时单条推送:

subscribeBar({ symbol, period, callback }) {
  // 有新数据时调用 callback(单条 KLineData)
}

推送规则(库内部自动处理):

  • 新 timestamp 大于最后一根 → 追加为新的一根;
  • 等于最后一根 → 覆盖最后一根(即当前周期的实时更新);
  • 小于 → 视为过期数据,忽略。

每次只推一条数据,且必须代表当前周期的那根 K 线。

unsubscribeBar:取消订阅

在 setSymbol / setPeriod / resetData / 销毁图表时触发。建议在 subscribeBar 里保存清理函数(如关闭 WebSocket),在 unsubscribeBar 里执行。


四、接入真实数据(REST + WebSocket)

一个完整的模板:

const stopMap = new Map()  // 保存每个订阅的清理函数

chart.setDataLoader({
  async getBars({ type, timestamp, symbol, period, callback }) {
    const res = await api.getKlineList({
      symbol: symbol.ticker,
      period: `${period.span}${period.type}`,
      endTime: timestamp ?? Date.now(),
      limit: 500,
      direction: type,
    })
    const bars = res.list
      .map(normalizeToKLineData)      // 见下方字段映射
      .sort((a, b) => a.timestamp - b.timestamp)
    callback(bars, {
      forward: res.hasMoreBefore,
      backward: res.hasMoreAfter,
    })
  },

  subscribeBar({ symbol, period, callback }) {
    const ws = createWsConnection(symbol.ticker, period)
    ws.onmessage = (message) => {
      callback(normalizeToKLineData(JSON.parse(message.data)))
    }
    stopMap.set(makeKey(symbol, period), () => ws.close())
  },

  unsubscribeBar({ symbol, period }) {
    const key = makeKey(symbol, period)
    stopMap.get(key)?.()
    stopMap.delete(key)
  },
})

后端字段与 KLineData 不一致时,先做映射:

// 后端:{ t: 1711425600, o: "68000.1", h: "68920.5", l: "67500.2", c: "68610.8", v: "1234.56" }
function normalizeToKLineData(d) {
  return {
    timestamp: d.t * 1000,      // 秒 → 毫秒
    open: Number(d.o),
    high: Number(d.h),
    low: Number(d.l),
    close: Number(d.c),
    volume: Number(d.v),
  }
}

五、技术指标

内置指标

库内置了 MA、EMA、BOLL、SAR、MACD、KDJ、RSI、VOL 等常用指标。查看当前版本支持的指标:

import { getSupportedIndicators } from 'klinecharts'
console.log(getSupportedIndicators())

添加 / 移除指标

import { createIndicator, removeIndicator } from 'klinecharts'

// 主图叠加 MA(isStack=true 表示叠加到已有副图/主图上)
chart.createIndicator('MA', true, { id: 'candle_pane' })

// 新建副图显示 MACD
chart.createIndicator('MACD', false, { id: 'pane_macd' })

// 移除
chart.removeIndicator('pane_macd', 'MACD')

自定义指标

import { registerIndicator, utils } from 'klinecharts'

registerIndicator({
  name: 'MyMA',
  shortName: 'MyMA',
  calcParams: [5, 10],                     // 可调参数
  figures: [
    { key: 'ma5', title: 'MA5: ', type: 'line' },
    { key: 'ma10', title: 'MA10: ', type: 'line' },
  ],
  calc: (dataList, indicator) => {
    const { calcParams } = indicator
    const closeSums = [0, 0]
    return dataList.map((kLineData, i) => {
      const ma = {}
      closeSums.forEach((sum, j) => {
        closeSums[j] = sum + kLineData.close
        if (i >= calcParams[j] - 1) {
          ma[`ma${calcParams[j]}`] = closeSums[j] / calcParams[j]
          closeSums[j] -= dataList[i - calcParams[j] + 1].close
        }
      })
      return ma
    })
  },
})

chart.createIndicator('MyMA', true, { id: 'candle_pane' })

六、样式配置

通过 setStyles 深度定制图表外观(支持部分更新):

chart.setStyles({
  grid: {
    show: true,
    horizontal: { color: '#333' },
    vertical: { show: false },
  },
  candle: {
    bar: {
      upColor: '#26a69a', upBorderColor: '#26a69a',
      downColor: '#ef5350', downBorderColor: '#ef5350',
    },
    priceMark: { high: { show: true }, low: { show: true } },
    tooltip: {
      showRule: 'always',   // 'always' | 'follow_cross' | 'none'
      text: { size: 12 },
    },
  },
  indicator: {
    bars: [{ key: '*', upColor: '#26a69a', downColor: '#ef5350' }],
    lines: [{ size: 1, style: 'solid' }],
  },
  crosshair: {
    show: true,
    horizontal: { text: { show: true } },
  },
})

暗色主题:

chart.setStyles('dark')   // 内置 'light' / 'dark' 两种预设

七、画线与标注(Overlay)

内置画线工具:线段、矩形、平行线、斐波那契回调、简单标注等。

import { createOverlay, removeOverlay, getOverlayClass } from 'klinecharts'

// 简单标注(如买卖点提示)
chart.createOverlay({ name: 'simpleAnnotation', points: [{ timestamp: 1517932800000, value: 8165.4 }] })

// 线段
chart.createOverlay({
  name: 'segment',
  points: [
    { timestamp: 1517846400000, value: 6032.3 },
    { timestamp: 1518624000000, value: 9774 },
  ],
})

// 移除全部
chart.removeOverlay()

八、常用 API 速查

API 说明
init(dom, options?) 初始化图表,返回图表实例
dispose(dom | instance) 销毁图表
setSymbol({ ticker, pricePrecision?, volumePrecision? }) 设置标的
setPeriod({ span, type }) 设置周期,type:second/minute/hour/day/week/month/year
setDataLoader(loader) 设置数据加载器
resetData() 重新拉取数据
getSymbol() / getPeriod() 获取当前标的 / 周期
setStyles(styles | 'light' | 'dark') 设置样式 / 主题
getStyles() 获取样式
createIndicator(name, isStack?, paneOptions?) 创建指标
removeIndicator(paneId, name) 移除指标
getSupportedIndicators() 所有支持的指标
registerIndicator(config) / unregisterIndicator(name) 注册 / 注销自定义指标
createOverlay(overlay) / removeOverlay(...) 画线 / 标注
setPaneOptions({ id, height?, state? }) 设置窗格
scrollToTimestamp(ts) / scrollToDataIndex(i) 滚动定位
subscribeAction(type, callback) 订阅事件:'tooltip'、'crosshair'、'pane'、'candle_bar' 等
setPriceVolumePrecision(pricePrecision, volumePrecision) 设置价格成交量精度
registerLocale(locale, messages) 注册语言包(i18n)

九、常见问题(FAQ)

1. 蜡烛显示成一条线 / 价格全被抹平?
设置标的时没有指定精度:

chart.setSymbol({ ticker: 'TestSymbol', pricePrecision: 6, volumePrecision: 6 })

2. 图表空白不显示?

  • 容器必须有宽高(div 不能是 0×0);
  • timestamp 必须是毫秒(后端秒级时间戳要 * 1000);
  • 数据必须按时间升序。

3. React 组件里图表初始化失败?
init 要在 DOM 挂载后调用(放在 useEffect 中,不要放在渲染期);卸载时调用 dispose。

4. 实时数据不更新?
subscribeBar 的 callback 每次只能推一条数据,且时间戳要 ≥ 最后一根;不要一次推一个数组。

5. 想开箱即用带顶栏和周期切换的完整版?
看官方基于 KLineChart 封装的 KLineChart Pro

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。