简介:kline.js 是一款用于金融数据可视化的 JavaScript 图表库,本压缩包围绕其用法与文档整理,面向需要绘制股票、期货等 K 线图的前端开发者,解决从引入库、初始化图表到配置交互和扩展指标的完整流程问题,并显著降低学习成本。包内共 26 个文件,约 3.62MB,包括 kline.js 主文件、CSS 样式、JSON 示例数据、多个 HTML 演示页面以及依赖的 js 库;其中示例覆盖 requirejs、轮询、WebSocket 等不同数据接入方式,还提供暗色与亮色主题截图,方便开发者直接对照运行。目前已吸引 224 人学习浏览。文档不仅详解基础概念、数据格式和配置项,也通过内置示例数据演示动态更新与动画效果,同时支持自定义 MACD、RSI 等指标,配合工程配置文件与说明文档,既适合新手入门,也能作为项目开发中的速查手册,帮助快速集成并定制专业级 K 线图表。
1. 为什么我还在用一个老牌 kline.js:一份压缩包就是一套可复现的行情图
做前端图表这些年,我见过太多号称“全功能”的图表框架,真到 K 线场景却要自己补数据精度、缩放、十字光标。直到拆开这份 js-KLine 压缩包,看到 kline-master 目录下规整的 kline.js 和 examples,我才确信老库的价值:它把金融行情图最脏的部分都处理完了。frighten9k3 的这份 kline.js 是一个轻量的 Web 端 K 线绘制库,你只需要喂给系统开高低收和成交量,就能拿到一张带缩放、十字光标、成交量副图的蜡烛图。它适合不想被重型图表库绑架的开发者,也适合需要在页面上快速落地一套可交互行情看板的前端工程师。这篇笔记我把初始化、配置、动态更新和踩坑全部记下来。
2. 初始化与数据接入:从压缩包结构到第一张 K 线图
2.1 目录结构透露出的依赖关系
解压 js-KLine.rar 之后,真正要用的核心是 kline-master 下的这几个东西:根目录的 kline.js、kline.css、package.json,以及 examples 目录下的 requirejs.html、polling.html、websocket.html 三个示例页。lib 文件夹里躺着 jquery.js、jquery.mousewheel.js、require.js、stomp.js、sockjs.js,这些依赖说明了一件事:这个库诞生在 jQuery 年代,滚轮缩放依赖 mousewheel 插件,实时行情示例走的是 SockJS 加 STOMP 协议。
我一般拿到压缩包不急着引文件,先看 examples 里的 HTML,因为示例页就是最真实的用法说明书。requirejs.html 演示 AMD 规范加载,polling.html 演示轮询拉数据,websocket.html 演示长连接推送,三个场景覆盖了行情页面最常见的接入方式。忽略 README 直接抄 examples 的写法,通常比看文档更快。
2.2 最小可运行示例:script 标签引入
在项目里引入老库最省心的方式,是直接在 HTML 里用 script 标签把依赖按顺序排好。KLine 这个全局变量在 kline.js 执行完之后挂到 window 上,所以顺序不能乱:
<div id="kline-container" style="width: 900px; height: 500px;"></div> <script src="lib/jquery.js"></script> <script src="lib/jquery.mousewheel.js"></script> <script src="kline.js"></script> <script> var container = document.getElementById('kline-container'); var kline = new KLine(container, { width: 900, height: 500 }); </script>这段代码的顺序是有讲究的:jquery.mousewheel.js 必须在 jquery.js 之后加载,否则插件没法挂到 jQuery 原型上;kline.js 内部会检测 jQuery 和 mousewheel 是否存在,缺失时图表能画出来,但滚轮缩放会静默失效,这一点我在避坑章节会展开。
容器 div 的宽高建议直接写在 style 里,或者像上面这样在构造参数里同时传一遍。老库初始化时会把容器内部替换成它自己生成的 DOM 结构,容器原有的子节点会被清空,所以不要在容器里预放别的元素用来做占位。
提示:容器内部的占位元素会被 new KLine 清空,不要在容器里预先放需要保留的节点。
2.3 数据格式:先对齐字段再谈画图
K 线图说到底画的是 OHLC 四价。这个库对数据数组的要求很明确:每个元素是一个对象,包含 time、open、high、low、close 这五个字段,成交量字段用 vol 或者 volume 都能识别,源码里做了兼容。mock.json 是压缩包里自带的示例数据,结构基本长这样:
[ { "time": 1483228800000, "open": 11300, "high": 11580, "low": 11250, "close": 11500, "vol": 2300 } ]time 字段建议传毫秒时间戳。我见过有人把时间格式化成字符串传进去,结果时间轴全乱,因为库内部会把这个值直接丢给 Date 处理。字符串格式不是完全不可用,但这种可复现的歧义最好在一开始就避开。
数据就绪之后调用 setData 填入图表:
kline.setData(mockData);setData 是整段替换语义,传入多少根画多少根。它不校验升序,但时间轴从左到右默认按时间递增展示,所以你在外部把数据排好序再传进来,比依赖库内部排序可靠得多。
3. 配置与交互:setConfig 参数表、点击事件与主题切换
3.1 setConfig 里最值得动的几个参数
初始化之后的第一件事通常是调样式。kline.js 把配置收敛在 setConfig 方法里,颜色、网格、成交量副图、时间格式都走这一条通道。我按实际项目里动得最多的参数列个表:
| 配置项 | 作用 | 常见取值 |
|---|---|---|
| color.up | 上涨蜡烛颜色 | #e03c3c(A股红) |
| color.down | 下跌蜡烛颜色 | #0aa869(A股绿) |
| grid.show | 是否显示网格线 | true / false |
| volume.show | 是否显示成交量副图 | true / false |
| timeFormat | 时间轴刻度格式 | %Y-%m-%d %H:%M |
kline.setConfig({ color: { up: '#e03c3c', down: '#0aa869' }, grid: { show: true }, volume: { show: true }, timeFormat: '%Y-%m-%d %H:%M' });中国业务里我习惯显式把 up 设成红色、down 设成绿色,不管库默认是什么配色,显式配置能避免和用户认知冲突。另外 timeFormat 只影响轴上显示的刻度文本,不会改数据本身的毫秒精度。
3.2 事件系统:click 和 crosshair 联动
这个库的事件挂在 on 方法上,支持的事件类型不多,但足以支撑常见交互。点击回调里会把当前那一根 K 线的完整数据原样返回,这在做“点击 K 线同步右侧订单簿”这类联动时非常顺手:
kline.on('click', function(data) { // data 里包含 time / open / high / low / close / vol console.log('点击位置对应的K线:', data); }); kline.on('crosshair', function(data) { // 十字光标移动时触发,适合高亮当前时刻 updateSidePanel(data.time); });click 和 crosshair 的区别在于:click 是按下抬起之后触发,crosshair 是鼠标在图表上移动时反复触发。如果你只需要一个高亮效果,优先用 crosshair,否则频繁 move 事件配合重渲染会拖慢页面。
事件回调里拿到的 data 对象是库内部维护的副本还是引用,我建议按只读来用。真要改数据,还是走 setData 或 addData,避免直接改内部对象导致渲染状态错乱。
3.3 深浅主题切换:css class 比 setConfig 更省事
压缩包里带了两张截图,screenshot_light.png 和 screenshot_dark.png,说明这个库天然支持深浅两套主题。翻 kline.css 能看到主题相关的 class 基本都集中在 .kline 容器下。切换主题最常见的做法是给容器动态挂一个 class,然后覆写 kline.css 里对应的背景色和网格线颜色:
.kline-dark .kline-grid-line { stroke: #2a2a2a; } .kline-dark { background: #1c1c1c; }document.getElementById('kline-container') .classList.toggle('kline-dark');我一般不建议在应用运行中反复 setConfig 换主题,因为 setConfig 内部要做一次整体重绘,频繁调用会带来肉眼可见的闪烁。CSS 覆写只改样式不改数据,视觉切换反而更平滑。
4. 增量更新与指标扩展:addData、indicators 和动画开关的边界
4.1 addData 增量追加重放实时行情
做行情页最常遇到的需求不是一次性画完,而是数据一朵一朵地来。addData 就是干这个的:它把新一根 K 线追加到序列末尾,而不是像 setData 那样整段替换。第二个参数控制是否播动画:
kline.addData({ time: Date.now(), open: 12300, high: 12400, low: 12200, close: 12380, vol: 500 }, true);第二个参数传 true 时,新 K 线会有一个从右往左滑入的动画,视觉上很像真实行情在流动。但这个动画只对单根追加友好,如果你一次性推过来几十根历史数据,逐根 addData 加 true 会卡到怀疑人生。正确的做法是历史数据用 setData 整段灌入,只有新产生的那一根才走 addData。
4.2 setData 与 addData 的边界怎么划
我一般按这么两条规则区分:
- 切换标的、切换周期、刷新历史:用 setData 全量替换。
- 同一标的同一周期下的增量 tick:用 addData 逐根追加。
如果后端接口设计成每次返回全量数据,那也没必要硬套 addData,直接 setData 重算即可。这个库并没有做增量合并的智能去重,addData 传进去的时间戳如果小于最后一根的时间戳,图表上就会出现一根时间倒序的异常 K 线,这是数据源要做好的功课。
4.3 自定义指标注册的通用路子
摘要里提到的 kline.indicators 接口,在不同版本里名字可能略有出入,有的版本叫 registerIndicator。拿到压缩包之后先搜一下源码里的 indicators 关键字,看当前版本的注册函数是什么,再照着下面这个套路写:
kline.indicators.myMA = function(dataList) { var result = []; for (var i = 0; i < dataList.length; i++) { if (i < 5) { result.push(null); continue; } var sum = 0; for (var j = i - 4; j <= i; j++) { sum += dataList[j].close; } result.push(sum / 5); } return result; };kline.setIndicator('myMA', { color: '#f59e0b', lineWidth: 1 });这里的关键点是:指标函数接收的 dataList 是整个序列,返回的数组必须和输入等长;前几根算不出指标值的位置用 null 占位,否则索引对不上,画出来的曲线整体左移。指标跑通之后,setIndicator 的第二个参数控制线的颜色和粗细。
如果是从 requirejs.html 里学的 AMD 用法,自定义指标写完后记得在回调里重新 setIndicator,因为 AMD 回调里的 KLine 实例和你外层全局变量可能不是同一个句柄。
5. 避坑指南:kline.js 五个典型故障的现象、原因与解法
这一章是我复现过程中挨打换来的血泪经验,每条按现象、原因、解法三段写,看完能少走不少弯路。
5.1 图表区域空白,控制台没有任何报错
现象:页面打开,容器 div 的高度塌陷成 0,图表压根不出现,但控制台安静得可疑。
原因:容器在初始化瞬间宽高为 0,或者容器 css 设置了 display:none。KLine 构造时会读取容器尺寸来创建内部画布,0 宽高也能构造成功,只是画出来之后看不见,所以不报错。
解决:初始化前确保容器可见且有明确尺寸。拿捏不准就在 new KLine 之前用 getBoundingClientRect 打点日志,确认宽高大于 0。切换 tab 时如果图表容器被 display:none 过,切回来之后手动触发一次 resize 重算尺寸。
5.2 图表能画出来,但滚轮缩放完全没反应
现象:蜡烛图正常,鼠标滚轮在图表上滚动,时间轴纹丝不动。
原因:kline.js 的缩放功能依赖 jquery.mousewheel.js,加载顺序一乱或者漏引了这个文件,库照常初始化,但缩放事件根本没人接管。
解决:把 lib 目录下两个文件按 jquery.js 在前、jquery.mousewheel.js 在后的顺序引入。如果项目用的是打包工具,把这两个文件的全局情况处理好,别让 mousewheel 被 tree-shaking 掉。
5.3 时间轴整体偏移,K 线日期对不上真实行情
现象:图表里最新一根 K 线的时间永远落后真实时间几个小时,或者全部跑到一天之前。
原因:时间戳单位不一致。后端返回秒级时间戳,库按毫秒解析,时间自然缩水 1000 倍;反过来前端把毫秒当秒传给后端也对不上。
解决:在数据接入层统一换算:
var row = rawData.map(function(item) { var ts = item.timestamp; if (String(ts).length === 10) { ts = ts * 1000; } return { time: ts, open: item.open, high: item.high, low: item.low, close: item.close, vol: item.volume }; });这段代码判断时间戳位数,10 位按秒转毫秒,13 位直接透传。时间基准定了,整张图的时间轴就稳了。
5.4 addData 之后视图跳回序列最左侧
现象:用户已经滚动到最新 K 线位置,新增一根数据后,视图一下子回到最早的那根,体验很糟糕。
原因:addData 内部如果触发的是全量重绘,会把显示区间重置为初始状态。这个行为在不同版本里略有差异。
解决:先在外部判断新数据的时间戳确实大于序列最后一根,再走 addData;如果库重置了视图,立即手动调用滚动到末尾的方法,或重新 setData 并指定显示最后 N 根。我一般会记录当前可视区间最后的时间戳,追加完成后把视图锚定回那个时间点。
5.5 自定义指标曲线与行情软件对不上
现象:自己写的 MA 指标画出来,和行情软件的同周期 MA 数值对不上。
原因:绝大多数情况不是库错了,而是源数据问题。行情软件默认展示前复权价,接口拿到的是不复权价,均值自然不同;另一个常见原因是周期口径不一致,比如用最近 5 根 1 分钟 K 线算 MA5,但软件里在日线图上算 MA5。
解决:先把数据源头统一成同一个复权口径,再对拍指标公式。对拍时不要一笔一笔对数,直接对比最近三根指标值和最后一根 K 线收盘价,能快速定位是数据错还是公式错。
6. 进阶玩法:把 polling 示例改成 websocket 实时行情
6.1 examples 里藏着的两种实时方案
polling.html 演示最朴素的实时方案:定时器每隔几秒拉一次接口,增量数据走 addData。websocket.html 则演示 SockJS 加 STOMP 订阅模型,lib 里的 stomp.js 和 sockjs.js 就是为它准备的。行情频率不高时 polling 够用,想上实时订阅就直接抄 websocket 示例的路子。
6.2 原生 WebSocket 接入写法
后端不搞 STOMP 时,原生 WebSocket 接同一套图表的要点在消息解析:
var ws = new WebSocket('wss://quote.example.com/kline'); ws.onmessage = function(event) { var tick = JSON.parse(event.data); kline.addData({ time: tick.time, open: tick.open, high: tick.high, low: tick.low, close: tick.close, vol: tick.vol }, true); };断线重连逻辑必须单独处理。长连接一断,中间缺的 K 线不会自动补,重连成功后先拉最近 100 根 setData 补齐,再恢复 addData,否则图表上出现数据空洞。
6.3 高频推送的降频合并
websocket 推送太密时,前端做 500ms 合并窗口,窗口内只保留最后一根 tick,窗口结束统一 addData 一次。K 线是聚合图形,降频不影响展示,反而让缩放更跟手。
从那以后,我每次接行情需求,都强制自己先写一张「数据字段 → 单位 → 时间基准」的对照表,再决定用 setData 还是 addData,最后才接图表库。这份 kline.js 的笔记能让你省下我当初踩的那些坑,希望帮到你。
本文还有配套的精品资源,点击获取