Add Y-axis (score) and X-axis (time) labels to forecast sparkline
This commit is contained in:
parent
88655e40a6
commit
09a26f1167
1 changed files with 24 additions and 11 deletions
|
|
@ -122,25 +122,27 @@ function buildLoadingHTML(detail) {
|
|||
function buildForecastSvg(forecast) {
|
||||
if (!forecast || forecast.length < 2) return ""
|
||||
|
||||
const w = 240, h = 48, pad = 2
|
||||
const marginL = 28, marginR = 6, marginT = 4, marginB = 16
|
||||
const w = 260, h = 72
|
||||
const plotW = w - marginL - marginR
|
||||
const plotH = h - marginT - marginB
|
||||
const n = forecast.length
|
||||
const now = new Date()
|
||||
const stepX = (w - pad * 2) / (n - 1)
|
||||
|
||||
const points = forecast.map((f, i) => {
|
||||
const x = pad + i * stepX
|
||||
const y = pad + (h - pad * 2) * (1 - f.score / 100)
|
||||
const x = marginL + (i / (n - 1)) * plotW
|
||||
const y = marginT + plotH * (1 - f.score / 100)
|
||||
return { x, y, score: f.score, time: new Date(f.time) }
|
||||
})
|
||||
|
||||
const polyline = points.map(p => `${p.x},${p.y}`).join(" ")
|
||||
|
||||
// Find the "now" index
|
||||
// "Now" dot
|
||||
const nowIdx = points.reduce((best, p, i) =>
|
||||
Math.abs(p.time - now) < Math.abs(points[best].time - now) ? i : best, 0)
|
||||
const nowPt = points[nowIdx]
|
||||
|
||||
// Trend arrow
|
||||
// Trend
|
||||
const firstScore = points[0].score
|
||||
const lastScore = points[points.length - 1].score
|
||||
const diff = lastScore - firstScore
|
||||
|
|
@ -149,9 +151,20 @@ function buildForecastSvg(forecast) {
|
|||
else if (diff < -5) trend = `<span style="color:#ff4f4f;">▼ Declining</span>`
|
||||
else trend = `<span style="color:#ffe566;">→ Steady</span>`
|
||||
|
||||
// Score labels at start and end
|
||||
const startLabel = `<text x="${pad}" y="${h - 2}" font-size="9" fill="rgba(255,255,255,0.5)">${firstScore}</text>`
|
||||
const endLabel = `<text x="${w - pad - 12}" y="${h - 2}" font-size="9" fill="rgba(255,255,255,0.5)">${lastScore}</text>`
|
||||
// Y-axis: score labels
|
||||
const yLabels = [0, 50, 100].map(s => {
|
||||
const y = marginT + plotH * (1 - s / 100)
|
||||
return `<text x="${marginL - 4}" y="${y + 3}" font-size="8" fill="rgba(255,255,255,0.4)" text-anchor="end">${s}</text>
|
||||
<line x1="${marginL}" y1="${y}" x2="${marginL + plotW}" y2="${y}" stroke="rgba(255,255,255,0.08)" stroke-width="1"/>`
|
||||
}).join("")
|
||||
|
||||
// X-axis: time labels (first, middle, last)
|
||||
const timeIdxs = [0, Math.floor(n / 2), n - 1]
|
||||
const xLabels = timeIdxs.map(i => {
|
||||
const p = points[i]
|
||||
const label = `${p.time.getUTCHours().toString().padStart(2, "0")}:00`
|
||||
return `<text x="${p.x}" y="${h - 2}" font-size="8" fill="rgba(255,255,255,0.4)" text-anchor="middle">${label}</text>`
|
||||
}).join("")
|
||||
|
||||
return `
|
||||
<div style="padding:6px 10px;border-top:1px solid rgba(255,255,255,0.15);">
|
||||
|
|
@ -160,10 +173,10 @@ function buildForecastSvg(forecast) {
|
|||
<span style="font-size:11px;">${trend}</span>
|
||||
</div>
|
||||
<svg width="${w}" height="${h}" style="display:block;">
|
||||
${yLabels}
|
||||
${xLabels}
|
||||
<polyline points="${polyline}" fill="none" stroke="#00ffa3" stroke-width="2" stroke-linejoin="round"/>
|
||||
<circle cx="${nowPt.x}" cy="${nowPt.y}" r="3" fill="#fff" stroke="#00ffa3" stroke-width="1.5"/>
|
||||
${startLabel}
|
||||
${endLabel}
|
||||
</svg>
|
||||
</div>`
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue