@chinese-fonts/yozai
Version:
Self-host the Chinese Web Fonts!
369 lines (357 loc) • 14.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<style>
body {
overflow-wrap: break-word;
font-size: 18px;
}
h3 {
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Open Sans',
'Helvetica Neue',
sans-serif;
font-weight: normal;
}
</style>
<body></body>
<script type="module">
import {
createSignal,
createResource,
onMount,
createEffect,
} from 'https://esm.sh/solid-js';
import html from 'https://esm.sh/solid-js/html';
import prettyBytes from 'https://esm.sh/pretty-bytes';
import { UnicodeRange } from 'https://esm.sh/@japont/unicode-range@1.0.0';
/** 延迟获取 Echarts*/
const getECharts = () =>
import('https://esm.sh/echarts@5.3.3/dist/echarts.esm.min.js');
const getFontData = (nameTable) =>
Object.fromEntries(
Object.entries(nameTable).map(([key, val]) => {
return [key, typeof val === 'string' ? val : val.en];
}),
);
const range = [
['基本汉字', 0x4e00, 0x9fa5],
['基本汉字补充', 0x9fa6, 0x9fff],
['扩展A', 0x3400, 0x4dbf],
['扩展B', 0x20000, 0x2a6df],
['扩展C', 0x2a700, 0x2b738],
['扩展D', 0x2b740, 0x2b81d],
['扩展E', 0x2b820, 0x2cea1],
['扩展F', 0x2ceb0, 0x2ebe0],
['扩展G', 0x30000, 0x3134a],
['康熙部首', 0x2f00, 0x2fd5],
['部首扩展', 0x2e80, 0x2ef3],
['兼容汉字', 0xf900, 0xfad9],
['兼容扩展', 0x2f800, 0x2fa1d],
['PUA(GBK)部件', 0xe815, 0xe86f],
['部件扩展', 0xe400, 0xe5e8],
['PUA增补', 0xe600, 0xe6cf],
['汉字笔画', 0x31c0, 0x31e3],
['汉字结构', 0x2ff0, 0x2ffb],
['汉语注音', 0x3105, 0x312f],
['注音扩展', 0x31a0, 0x31ba],
['〇', 0x3007, 0x3007],
];
const RangeAnalyze = (data) => {
const total = data.reduce((col, cur) => {
if (cur.chars.startsWith('U+')) {
return (
col +
String.fromCodePoint(
...UnicodeRange.parse(cur.chars.split(',')),
)
);
}
return col + cur.chars;
}, '');
const result = range.map(([name, min, max]) => {
let exist = '';
let voids = '';
for (let i = min; i <= max; i++) {
const char = String.fromCodePoint(i);
const isExist = total.includes(char);
if (isExist) {
exist += char;
} else {
voids += char;
}
}
return [name, exist, voids];
});
return html`
<table style="width:80%;margin:auto;padding:1rem">
<thead>
<tr>
<th>位置</th>
<th>存在</th>
<th>不存在</th>
<th>覆盖率</th>
</tr>
</thead>
${result.map(([name, exist, voids]) => {
const coverage =
(exist.length * 100) /
(exist.length + voids.length);
return html`
<tr>
<td>${name}</td>
<td>${exist.length}</td>
<td>${voids.length}</td>
<td>${coverage.toFixed(2)}%</td>
</tr>
`;
})}
</table>
`;
};
const TimeAnalyze = (record, message) => {
record.pop(); // 最后一个记录是没有用的
const total = record.reduce(
(col, cur) => col + cur.end - cur.start,
0,
);
let chartDom;
// console.log(record, total);
onMount(() => {
// 问就是渲染代价太大,等 1000ms 让浏览器冷静一下
setTimeout(async () => {
const echarts = await getECharts();
let myChart = echarts.init(chartDom);
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
title: {
text: '打包时间分布图',
subtext: `时间为 ms; 总时间 ${total} ms;\n${message.fontFamily}`,
},
legend: {
top: '15%',
},
grid: {
left: '10%',
right: '10%',
bottom: '30%',
top: '30%',
},
xAxis: {
type: 'value',
},
yAxis: {
type: 'category',
data: ['时间轴'],
},
series: record.map((i) => {
return {
name: i.name,
type: 'bar',
stack: 'total',
label: {
show: true,
},
emphasis: {
focus: 'series',
},
data: [
parseFloat((i.end - i.start).toFixed(1)),
],
};
}),
};
option && myChart.setOption(option);
}, 1500);
});
return html`<div
ref=${function (dom) {
chartDom = dom;
}}
style="width: 600px;height:400px;margin:auto"
></div>`;
};
const DataAnalyze = (data, message) => {
const total = data.reduce((col, cur) => col + cur.size, 0);
// console.log(data, total);
let chartDom;
onMount(() => {
// 问就是渲染代价太大,等 1000ms 让浏览器冷静一下
setTimeout(async () => {
const echarts = await getECharts();
let myChart = echarts.init(chartDom);
let option = {
tooltip: {
trigger: 'item',
formatter(data) {
return (
`第 ${data.dataIndex + 1} 分包\n` +
data.data.name +
'\n' +
prettyBytes(data.data.value)
);
},
},
title: {
text: message.fontFamily,
subtext: `总共 ${
data.length
} 分包; 总大小 ${prettyBytes(
total,
)} 点击跳转查看;`,
left: 'center',
},
series: [
{
name: '分包信息',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2,
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold',
},
},
labelLine: {
show: true,
},
label: {
show: true,
minMargin: 5,
edgeDistance: 10,
lineHeight: 15,
formatter(data) {
return (
(
(data.data.value * 100) /
total
).toFixed(2) + '%'
);
},
},
data: data.map((i) => ({
value: i.size,
name: i.name.slice(0, 7),
hash: i.name,
})),
},
],
};
option && myChart.setOption(option);
myChart.on('click', (data) => {
document
.getElementById(data.data.hash)
.scrollIntoView();
});
}, 300);
});
return html`<div
ref=${function (dom) {
chartDom = dom;
}}
style="width: 600px;height:600px;margin:auto"
></div>`;
};
const CharList = (data) => {
return data.map((i) => {
const chars = i.chars.startsWith('U+')
? String.fromCodePoint(
...UnicodeRange.parse(i.chars.split(',')),
)
: i.chars;
return html`<div>
<h3 id="${i.name}">
分片名称 ${i.name} | 分片大小 ${prettyBytes(i.size)}
</h3>
<p>${chars}</p>
</div>`;
});
};
const BaseMessage = (message) => {
return html`
<table style="margin:auto">
${Object.entries(message).map((i) => {
return html`
<tr>
<td>${i[0].en}</td>
<td>${i[1].en}</td>
</tr>
`;
})}
</table>
`;
};
const App = () => {
const [data] = createResource(() =>
fetch('./reporter.json')
.then((res) => res.json())
.then((res) => {
res.message =
res.message.windows || res.message.macintosh;
return res;
}),
);
createEffect(() => {
if (data()) {
console.log(data().message);
document.body.style.fontFamily = `"${
getFontData(data().message).fontFamily ||
getFontData(data().message).preferredFamily
}"`;
document.querySelector('title').textContent = getFontData(
data().message,
).fontFamily;
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href =
'./' + (data().config.cssFileName || 'result') + '.css';
document.head.appendChild(link);
}
});
const content = () =>
html` <div>
${RangeAnalyze(
data().data,
getFontData(data().message),
)}
${DataAnalyze(data().data, getFontData(data().message))}
${TimeAnalyze(
data().record,
getFontData(data().message),
)}
${BaseMessage(getFontData(data().message))}
</div>
${CharList(data().data)}`;
return html`<div>
${() => (data.loading ? `<div>加载中</div>` : content())}
</div>`;
};
import { render } from 'https://esm.sh/solid-js/web';
render(App, document.body);
</script>
</html>