JSFiddle - React, Tailwind, and code Playground
HTML
<link rel="stylesheet" href="https://leeoniya.github.io/uPlot/dist/uPlot.min.css">
<script src="https://leeoniya.github.io/uPlot/dist/uPlot.iife.min.js"></script>
<script>
// gold prices from https://www.investing.com/commodities/gold-historical-data
// Date,Open,High,Low,Close
const data =...
JavaScript
function fmtUSD(val, dec) {
return "$" + val.toFixed(dec).replace(/\d(?=(\d{3})+(?:\.|$))/g, "$&,");
}
// draws candlestick symbols (expects data in OHLC order)
function candlestickPlugin({ gap = 2, shadowColor = "#000000", bearishColor = "#e54245", bullishColor = "#4ab650", bodyMaxWidth = 20, shadowWidth = 2, bodyOutline = 1 } = {}) {
function drawCandles(u) {
u.ctx.save();
const offset = (shadowWidth % 2) / 2;
u.ctx.translate(offset, offset);
let [iMin, iMax] = u.series[0].idxs;
for (let i = iMin; i <= iMax; i++) {
let xVal = u.scales.x.distr == 2 ? i : u.data[0][i];
let open = u.data[1][i];
let high = u.data[2][i];
let low = u.data[3][i];
let close = u.data[4][i];
let timeAsX = u.valToPos(xVal, "x", true);
let lowAsY = u.valToPos(low, "y", true);
let highAsY = u.valToPos(high, "y", true);
let openAsY = u.valToPos(open, "y", true);
let closeAsY = u.valToPos(close, "y", true);
// shadow rect
let shadowHeight = Math.max(highAsY, lowAsY) - Math.min(highAsY, lowAsY);
let shadowX = timeAsX - (shadowWidth / 2);
let shadowY = Math.min(highAsY, lowAsY);
u.ctx.fillStyle = shadowColor;
u.ctx.fillRect(
Math.round(shadowX),
Math.round(shadowY),
Math.round(shadowWidth),
Math.round(shadowHeight),
);
// body rect
let columnWidth = u.bbox.width / (iMax - iMin);
let bodyWidth = Math.min(bodyMaxWidth, columnWidth - gap);
let bodyHeight = Math.max(closeAsY, openAsY) - Math.min(closeAsY, openAsY);
let bodyX = timeAsX - (bodyWidth / 2);
let bodyY = Math.min(closeAsY, openAsY);
let bodyColor = open > close ? bearishColor : bullishColor;
u.ctx.fillStyle = shadowColor;
u.ctx.fillRect(
Math.round(bodyX),
Math.round(bodyY),
...