stats/lib/svg.js

155 lines
3.6 KiB
JavaScript
Raw Normal View History

2023-06-07 19:01:53 +08:00
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/06/07 18:41:20
*/
function html(strs, ...vals) {
let output = ''
for (let it of strs) {
2023-06-08 12:45:35 +08:00
output += it + (vals.shift() ?? '')
2023-06-07 19:01:53 +08:00
}
return output
}
2023-06-08 12:45:35 +08:00
function text({ color, name, pc, idx }) {
2023-06-09 11:21:48 +08:00
let y = 32 + ~~(idx / 2 + 1) * 32
let x1 = idx % 2 === 0 ? 12 : 212
let x2 = idx % 2 === 0 ? 24 : 224
2023-06-08 12:45:35 +08:00
return html`
<g class="lang">
<circle cx="${x1}" cy="${y}" r="5" fill="${color}" />
<text x="${x2}" y="${y + 4}">${name} ${pc}%</text>
</g>
`
}
2023-09-11 16:42:35 +08:00
function pie(langs = [], sum = 0, width, height, mobile) {
2023-06-08 12:45:35 +08:00
let per = 0.25
let deg = per * 2 * Math.PI // 从90度开始计算
2023-06-09 11:21:48 +08:00
// 圆心坐标,半径
let cx, cy, radius
if (mobile) {
radius = 72
cx = 192 - 16
cy = ~~(langs.length / 2 + 1) * 32 + 32 + radius
} else {
radius = ~~((height - 64) / 2) - (langs.length > 10 ? 24 : 0) // 最小半径
cx = langs.length > 12 ? 520 : langs.length <= 6 ? 384 : 464
cy = radius + (langs.length > 10 ? 64 : 48)
2023-09-11 16:42:35 +08:00
let _cx = width - radius * Math.pow(1.05, langs.length)
if (_cx - cx > 10) {
cx = _cx - 10
}
2023-06-09 11:21:48 +08:00
}
2023-06-08 12:45:35 +08:00
return langs
.map((it, idx) => {
// 扇形起始坐标
let r = +radius.toFixed(3)
let lx = +(cx + Math.sin(deg) * r).toFixed(3)
let ly = +(cy - Math.cos(deg) * r).toFixed(3)
let nx, ny
let _per = +(it.size / sum).toFixed(4)
radius *= 1.05
per += _per
deg = per * 2 * Math.PI
nx = +(cx + Math.sin(deg) * r * (per > 0.5 ? 1 : -1)).toFixed(3)
ny = +(cy - Math.cos(deg) * r).toFixed(3)
return html`
<path
class="pie pie-${idx}"
d="M${cx} ${cy} L${lx} ${ly} A${r} ${r} 0 ${_per > 0.5
? 1
: 0} 1 ${nx} ${ny} Z"
fill="${it.color}"
/>
`
})
.join('')
}
2023-06-09 11:21:48 +08:00
export function render(
{ title = 'Most Used Languages', langs = [] } = {},
mobile = false
) {
2023-06-08 12:45:35 +08:00
let sum = langs.reduce((n, it) => n + it.size, 0)
2023-06-09 11:21:48 +08:00
let height = ~~(langs.length / 2 + 1) * 32 + 32
let width = 736
2023-06-08 12:45:35 +08:00
if (height < 140) {
height = 140
}
2023-06-09 11:21:48 +08:00
if (langs.length < 5) {
2023-09-11 16:42:35 +08:00
width -= 256
2023-06-09 11:21:48 +08:00
} else if (langs.length > 4 && langs.length < 7) {
width -= 240
} else if (langs.length > 6 && langs.length <= 12) {
width -= 144
}
if (mobile) {
width = 384
height = ~~(langs.length / 2 + 1) * 32 + 36 + 72 * 2
}
2023-06-07 19:01:53 +08:00
return html`
<svg
2023-06-09 11:21:48 +08:00
viewBox="0 0 ${width} ${height}"
2023-06-07 19:01:53 +08:00
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<style>
.header {
2023-06-08 12:45:35 +08:00
font: bold 22px 'Segoe UI', Ubuntu, Sans-Serif;
2023-06-07 19:01:53 +08:00
fill: #64b5f6;
}
2023-06-08 12:45:35 +08:00
.lang {
font: 14px Menlo, Monaco, Consolas, 'Courier New', monospace;
fill: #64748b;
}
2023-06-08 12:49:01 +08:00
.lang-pie {
fill: #64748b;
}
2023-06-08 12:45:35 +08:00
.pie {
opacity: 0;
animation: fade 0.5s ease-in-out forwards;
}
${langs
.map((it, i) => `.pie-${i} {animation-delay: ${i * 100}ms}`)
.join('\n')}
@keyframes
fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
2023-06-07 19:01:53 +08:00
</style>
2023-06-09 11:21:48 +08:00
<g transform="translate(6, 32)">
2023-06-07 19:01:53 +08:00
<text class="header">${title}</text>
</g>
2023-06-08 12:45:35 +08:00
<g class="lang-list">
${langs
.map((it, idx) =>
text({
color: it.color,
name: it.name,
pc: +((100 * it.size) / sum).toFixed(2),
idx
})
)
.join('')}
</g>
2023-09-11 16:42:35 +08:00
<g class="lang-pie">${pie(langs, sum, width, height, mobile)}</g>
2023-06-07 19:01:53 +08:00
</svg>
`
}