@chinese-fonts/cezkzdbs
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>