@shauny/chart
v0.1.1
Published
Lightweight TradingView-like chart engine core
Readme
@shauny/chart
A lightweight candlestick chart engine built on Canvas 2D with a composable layer architecture. Supports pan, zoom, crosshair, and real-time updates with zero dependencies. Add custom indicators instantly via addLayer().
React bindings are available separately as @shauny/chart-react. The core works framework-agnostically.
Installation
npm install @shauny/chartBasic Usage
import { Chart } from '@shauny/chart'
import type { Candle } from '@shauny/chart'
const canvas = document.getElementById('chart') as HTMLCanvasElement
const chart = new Chart(canvas, {
background: '#131722',
grid: {
lineColor: '#1e2530',
labelColor: '#787b86',
},
})
const candles: Candle[] = [
{ time: 1700000000000, open: 100, high: 110, low: 95, close: 105, volume: 1000 },
{ time: 1700086400000, open: 105, high: 115, low: 100, close: 112, volume: 1200 },
// time is Unix ms, must be sorted in ascending order
]
chart.setData(candles)
chart.start()
// on unmount
chart.destroy()ChartOptions
interface ChartOptions {
background?: string // default: '#000000'
padding?: { top: number; right: number; bottom: number; left: number }
minZoom?: number // minimum zoom scale
maxZoom?: number // maximum zoom scale
grid?: {
lineColor?: string // grid line color (default: '#1e2530')
labelColor?: string // axis label color (default: '#787b86')
borderColor?: string // axis border color (default: '#2a2e39')
}
}API
chart.setData(candles, preserveViewport?) // load data; pass true to keep current zoom/pan
chart.start() // start RAF loop
chart.destroy() // clean up events, RAF loop, and ResizeObserver
chart.fitToData() // reset viewport to show all data
chart.addLayer(layer) // add a custom layer on top
chart.removeLayer(layer) // remove a layer
chart.visibleRange // current visible { xMin, xMax, yMin, yMax }Real-time Updates
Pass true as the second argument to setData to preserve the current zoom and pan position while replacing data.
function onTick(candles: Candle[]) {
chart.setData(candles, true)
}Custom Layers
Implement the Layer interface to render anything on top of the chart. Below is an example that draws a line connecting closing prices.
import type { Layer, ViewportRange, CanvasSize, Candle } from '@shauny/chart'
class ClosePriceLineLayer implements Layer {
constructor(private candles: Candle[]) {}
draw(ctx: CanvasRenderingContext2D, viewport: ViewportRange, size: CanvasSize) {
if (this.candles.length === 0) return
const chartW = size.width - 60 // subtract 60px for right price axis
const chartH = size.height - 24 // subtract 24px for bottom time axis
const xSpan = viewport.xMax - viewport.xMin
const ySpan = viewport.yMax - viewport.yMin
const toX = (t: number) => ((t - viewport.xMin) / xSpan) * chartW
const toY = (p: number) => ((viewport.yMax - p) / ySpan) * chartH
// note: y-axis is inverted — higher prices map to smaller pixel y values
ctx.beginPath()
ctx.strokeStyle = '#f0b90b'
ctx.lineWidth = 1.5
for (let i = 0; i < this.candles.length; i++) {
const x = toX(this.candles[i].time)
const y = toY(this.candles[i].close)
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y)
}
ctx.stroke()
}
}
// register
chart.addLayer(new ClosePriceLineLayer(candles))License
MIT
