Spaces:
Running
Running
Download solana-clock/js/chart.js from Ethscriptions/POLS: direct link, hf CLI and curl.
- Browser
- Download file 5.41 kB
-
https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/solana-clock/js/chart.js
- Command line
-
hf download hf://spaces/Ethscriptions/POLS/solana-clock/js/chart.js
-
curl -L -o chart.js https://huggingface.co/spaces/Ethscriptions/POLS/resolve/main/solana-clock/js/chart.js
5.41 kB
| // Real SOL price chart renderer (data snapshot from Surf API) | |
| import { SOL_PRICES, SOL_T0 } from './data-sol.js'; | |
| import { clamp, lerp, glow, text, hexA, fmtUSD, F_MONO, GREEN, PURPLE, RED, WHITE } from './draw.js'; | |
| export const idx = (date) => clamp(Math.round((Date.parse(date + 'T00:00:00Z') / 1000 - SOL_T0) / 86400), 0, SOL_PRICES.length - 1); | |
| export const price = (i) => SOL_PRICES[clamp(Math.round(i), 0, SOL_PRICES.length - 1)]; | |
| export const dateOf = (i) => new Date((SOL_T0 + Math.round(i) * 86400) * 1000).toISOString().slice(0, 10); | |
| export const LAST = SOL_PRICES.length - 1; | |
| /** interpolated price at fractional index */ | |
| export function priceF(f) { | |
| const i = Math.floor(f), k = f - i; | |
| return lerp(price(i), price(i + 1), k); | |
| } | |
| /** | |
| * Draw a chart. rect {x,y,w,h}. from/to: day indices. prog: 0..1 drawn fraction. | |
| * Returns head {x,y,v,i,slope}. | |
| */ | |
| export function drawChart(ctx, rect, o) { | |
| const { from, to, prog = 1, log = false, color = GREEN, color2 = PURPLE, alpha = 1, grid = true, yMin, yMax, lw = 5, fill = true, head = true, headLabel = true, dates = true, gridVals } = o; | |
| const n = to - from; | |
| let lo = Infinity, hi = -Infinity; | |
| for (let i = from; i <= to; i++) { const v = price(i); if (v < lo) lo = v; if (v > hi) hi = v; } | |
| lo = yMin ?? lo * (log ? .8 : .9); hi = yMax ?? hi * 1.08; | |
| if (!log) lo = yMin ?? Math.max(0, lo - (hi - lo) * .05); | |
| const Y = v => log ? rect.y + rect.h - (Math.log(v) - Math.log(lo)) / (Math.log(hi) - Math.log(lo)) * rect.h : rect.y + rect.h - (v - lo) / (hi - lo) * rect.h; | |
| const X = i => rect.x + (i - from) / n * rect.w; | |
| ctx.save(); ctx.globalAlpha *= alpha; | |
| if (grid) { | |
| const vals = gridVals || (log ? [1, 10, 100] : niceTicks(lo, hi)); | |
| ctx.lineWidth = 1; | |
| for (const v of vals) { | |
| if (v < lo || v > hi) continue; | |
| const y = Y(v); | |
| ctx.strokeStyle = 'rgba(255,255,255,.09)'; ctx.setLineDash([6, 8]); ctx.beginPath(); ctx.moveTo(rect.x, y); ctx.lineTo(rect.x + rect.w, y); ctx.stroke(); ctx.setLineDash([]); | |
| text(ctx, fmtUSD(v), rect.x - 12, y, { size: 20, fam: F_MONO, weight: 500, color: 'rgba(255,255,255,.45)', align: 'right' }); | |
| } | |
| if (dates) { | |
| const y0 = new Date((SOL_T0 + from * 86400) * 1000).getUTCFullYear(), y1 = new Date((SOL_T0 + to * 86400) * 1000).getUTCFullYear(); | |
| for (let yr = y0; yr <= y1 + 1; yr++) { | |
| const i = idx(yr + '-01-01'); if (i <= from || i >= to) continue; | |
| const x = X(i); | |
| ctx.strokeStyle = 'rgba(255,255,255,.08)'; ctx.beginPath(); ctx.moveTo(x, rect.y); ctx.lineTo(x, rect.y + rect.h); ctx.stroke(); | |
| text(ctx, String(yr), x, rect.y + rect.h + 22, { size: 20, fam: F_MONO, weight: 600, color: 'rgba(255,255,255,.5)', align: 'center' }); | |
| } | |
| } | |
| } | |
| const endF = from + n * clamp(prog); | |
| if (endF > from) { | |
| const step = Math.max(1, Math.floor(n / 900)); | |
| ctx.beginPath(); | |
| let first = true, lx = 0, ly = 0; | |
| for (let i = from; i <= Math.floor(endF); i += step) { const x = X(i), y = Y(price(i)); if (first) { ctx.moveTo(x, y); first = false; } else ctx.lineTo(x, y); lx = x; ly = y; } | |
| const hv = priceF(endF), hx = X(endF), hy = Y(hv); ctx.lineTo(hx, hy); | |
| const g = ctx.createLinearGradient(rect.x, 0, rect.x + rect.w, 0); g.addColorStop(0, color2); g.addColorStop(1, color); | |
| // fill | |
| if (fill) { | |
| ctx.save(); ctx.lineTo(hx, rect.y + rect.h); ctx.lineTo(X(from), rect.y + rect.h); ctx.closePath(); | |
| const fg = ctx.createLinearGradient(0, rect.y, 0, rect.y + rect.h); fg.addColorStop(0, hexA(color, .28)); fg.addColorStop(1, hexA(color, 0)); | |
| ctx.fillStyle = fg; ctx.fill(); ctx.restore(); | |
| // re-trace stroke path | |
| ctx.beginPath(); first = true; | |
| for (let i = from; i <= Math.floor(endF); i += step) { const x = X(i), y = Y(price(i)); if (first) { ctx.moveTo(x, y); first = false; } else ctx.lineTo(x, y); } | |
| ctx.lineTo(hx, hy); | |
| } | |
| ctx.lineJoin = 'round'; ctx.lineCap = 'round'; | |
| ctx.strokeStyle = hexA(color, .25); ctx.lineWidth = lw * 3.2; ctx.stroke(); | |
| ctx.strokeStyle = g; ctx.lineWidth = lw; ctx.stroke(); | |
| void lx; void ly; | |
| const sl = (Y(priceF(Math.min(endF + 3, to))) - Y(priceF(Math.max(endF - 3, from)))) / (X(Math.min(endF + 3, to)) - X(Math.max(endF - 3, from)) || 1); | |
| if (head && prog > 0) { | |
| glow(ctx, hx, hy, 60, color, .9); | |
| ctx.fillStyle = WHITE; ctx.beginPath(); ctx.arc(hx, hy, 8, 0, 7); ctx.fill(); | |
| if (headLabel) { | |
| const right = hx < rect.x + rect.w * .75; | |
| text(ctx, fmtUSD(hv), hx + (right ? 22 : -22), hy - 34, { size: 34, fam: F_MONO, weight: 800, color: WHITE, align: right ? 'left' : 'right', stroke: 6, strokeColor: 'rgba(0,0,0,.7)' }); | |
| text(ctx, dateOf(endF), hx + (right ? 22 : -22), hy - 2, { size: 20, fam: F_MONO, weight: 500, color: 'rgba(255,255,255,.6)', align: right ? 'left' : 'right' }); | |
| } | |
| } | |
| ctx.restore(); | |
| return { x: hx, y: hy, v: hv, i: endF, slope: sl, X, Y }; | |
| } | |
| ctx.restore(); | |
| return { x: X(from), y: Y(price(from)), v: price(from), i: from, slope: 0, X, Y }; | |
| } | |
| function niceTicks(lo, hi) { | |
| const span = hi - lo, raw = span / 4, mag = Math.pow(10, Math.floor(Math.log10(raw))); | |
| const step = [1, 2, 2.5, 5, 10].map(k => k * mag).find(s => span / s <= 5); | |
| const out = []; for (let v = Math.ceil(lo / step) * step; v <= hi; v += step) out.push(+v.toFixed(4)); | |
| return out; | |
| } | |
| export const RED_C = RED; | |