会员个人中心页面初步完成
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
const COLORS = {
|
||||
primary: '#0B2B4B',
|
||||
accent: '#FF6B35',
|
||||
accentLight: 'rgba(255, 107, 53, 0.25)',
|
||||
grid: '#E9EDF2',
|
||||
text: '#5E6F8D',
|
||||
fill: 'rgba(26, 74, 111, 0.35)',
|
||||
line: '#1A4A6F'
|
||||
}
|
||||
|
||||
function setupCanvas(canvas, width, height, dpr) {
|
||||
canvas.width = width * dpr
|
||||
canvas.height = height * dpr
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.scale(dpr, dpr)
|
||||
return ctx
|
||||
}
|
||||
|
||||
/** 绘制雷达图 */
|
||||
export function drawRadarChart(canvas, opts = {}) {
|
||||
if (!canvas) return
|
||||
const {
|
||||
width = 280,
|
||||
height = 240,
|
||||
labels = [],
|
||||
values = [],
|
||||
dpr = 1
|
||||
} = opts
|
||||
const ctx = setupCanvas(canvas, width, height, dpr)
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
const cx = width / 2
|
||||
const cy = height / 2 + 8
|
||||
const radius = Math.min(width, height) * 0.32
|
||||
const count = labels.length || 6
|
||||
const angleStep = (Math.PI * 2) / count
|
||||
|
||||
for (let level = 1; level <= 4; level += 1) {
|
||||
ctx.beginPath()
|
||||
const r = (radius * level) / 4
|
||||
for (let i = 0; i <= count; i += 1) {
|
||||
const angle = -Math.PI / 2 + i * angleStep
|
||||
const x = cx + r * Math.cos(angle)
|
||||
const y = cy + r * Math.sin(angle)
|
||||
if (i === 0) ctx.moveTo(x, y)
|
||||
else ctx.lineTo(x, y)
|
||||
}
|
||||
ctx.strokeStyle = COLORS.grid
|
||||
ctx.lineWidth = 1
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
const angle = -Math.PI / 2 + i * angleStep
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(cx, cy)
|
||||
ctx.lineTo(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle))
|
||||
ctx.strokeStyle = COLORS.grid
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
ctx.beginPath()
|
||||
values.forEach((val, i) => {
|
||||
const ratio = Math.min(1, Math.max(0, val / 100))
|
||||
const angle = -Math.PI / 2 + i * angleStep
|
||||
const x = cx + radius * ratio * Math.cos(angle)
|
||||
const y = cy + radius * ratio * Math.sin(angle)
|
||||
if (i === 0) ctx.moveTo(x, y)
|
||||
else ctx.lineTo(x, y)
|
||||
})
|
||||
ctx.closePath()
|
||||
ctx.fillStyle = COLORS.fill
|
||||
ctx.fill()
|
||||
ctx.strokeStyle = COLORS.accent
|
||||
ctx.lineWidth = 2
|
||||
ctx.stroke()
|
||||
|
||||
ctx.font = '11px sans-serif'
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.textAlign = 'center'
|
||||
labels.forEach((label, i) => {
|
||||
const angle = -Math.PI / 2 + i * angleStep
|
||||
const x = cx + (radius + 18) * Math.cos(angle)
|
||||
const y = cy + (radius + 18) * Math.sin(angle) + 4
|
||||
ctx.fillText(label, x, y)
|
||||
})
|
||||
}
|
||||
|
||||
/** 绘制折线趋势图 */
|
||||
export function drawTrendChart(canvas, opts = {}) {
|
||||
if (!canvas) return
|
||||
const {
|
||||
width = 300,
|
||||
height = 160,
|
||||
points = [],
|
||||
dpr = 1,
|
||||
unit = ''
|
||||
} = opts
|
||||
const ctx = setupCanvas(canvas, width, height, dpr)
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
if (!points.length) {
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.font = '13px sans-serif'
|
||||
ctx.textAlign = 'center'
|
||||
ctx.fillText('暂无趋势数据', width / 2, height / 2)
|
||||
return
|
||||
}
|
||||
|
||||
const pad = { top: 16, right: 12, bottom: 28, left: 12 }
|
||||
const chartW = width - pad.left - pad.right
|
||||
const chartH = height - pad.top - pad.bottom
|
||||
const values = points.map((p) => p.value)
|
||||
const min = Math.min(...values) * 0.95
|
||||
const max = Math.max(...values) * 1.05
|
||||
const range = max - min || 1
|
||||
|
||||
ctx.strokeStyle = COLORS.grid
|
||||
ctx.lineWidth = 1
|
||||
for (let i = 0; i <= 3; i += 1) {
|
||||
const y = pad.top + (chartH * i) / 3
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(pad.left, y)
|
||||
ctx.lineTo(width - pad.right, y)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
const coords = points.map((p, i) => ({
|
||||
x: pad.left + (chartW * i) / Math.max(1, points.length - 1),
|
||||
y: pad.top + chartH - ((p.value - min) / range) * chartH
|
||||
}))
|
||||
|
||||
ctx.beginPath()
|
||||
coords.forEach((pt, i) => {
|
||||
if (i === 0) ctx.moveTo(pt.x, pt.y)
|
||||
else ctx.lineTo(pt.x, pt.y)
|
||||
})
|
||||
ctx.strokeStyle = COLORS.line
|
||||
ctx.lineWidth = 2
|
||||
ctx.stroke()
|
||||
|
||||
coords.forEach((pt, i) => {
|
||||
ctx.beginPath()
|
||||
ctx.arc(pt.x, pt.y, 4, 0, Math.PI * 2)
|
||||
ctx.fillStyle = COLORS.accent
|
||||
ctx.fill()
|
||||
ctx.strokeStyle = '#fff'
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.stroke()
|
||||
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.font = '10px sans-serif'
|
||||
ctx.textAlign = 'center'
|
||||
ctx.fillText(points[i].label, pt.x, height - 8)
|
||||
})
|
||||
|
||||
if (unit) {
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.font = '10px sans-serif'
|
||||
ctx.textAlign = 'left'
|
||||
ctx.fillText(unit, pad.left, pad.top - 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user