From 975134fb142131d719feaba848d4ec7a65f97e2f Mon Sep 17 00:00:00 2001 From: SeaYJ Date: Thu, 3 Oct 2024 23:41:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9=20ChartJS=20?= =?UTF-8?q?=E5=8F=8C=E6=A8=A1=E5=BC=8F=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加了 ChartJS 图表官方语法; 2. 新增了 ChartJS 的双模式(浅色/深色模式)显示功能; 3. 扩展了 ChartJS 的 config 语法,支持双模式配置。 4. 修复了 ChartJS 在双模式下默认样式的问题,简化用户配置图表过程。 --- _config.yml | 18 +++++ layout/includes/third-party/math/chartjs.pug | 79 ++++++++++++++++++++ layout/includes/third-party/math/index.pug | 5 +- plugins.yml | 4 + scripts/events/merge_config.js | 19 +++++ scripts/tag/chartjs.js | 39 ++++++++++ source/css/_layout/third-party.styl | 4 + 7 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 layout/includes/third-party/math/chartjs.pug create mode 100644 scripts/tag/chartjs.js diff --git a/_config.yml b/_config.yml index a448599..98458ee 100644 --- a/_config.yml +++ b/_config.yml @@ -924,6 +924,24 @@ mermaid: light: default dark: dark +# chartjs +# see https://www.chartjs.org/docs/latest/ +chartjs: + enable: true + # 下面的設置是正對 ChartJS 的預設樣式設置,非必要請勿修改,除非你理解他們是如何工作的 + # 當然,樣式會以具體的 MD 語法設置優先,其次為該預設設置,最後為 ChartJS 本身的預設設置 + color: # 表格通用字體顏色 + light: "rgba(0, 0, 0, 0.8)" # 淺色模式 + dark: "rgba(255, 255, 255, 0.8)" # 深色模式 + borderColor: # 表格通用邊框顏色 + light: "rgba(0, 0, 0, 0.1)" + dark: "rgba(255, 255, 255, 0.2)" + scale: + ticks: + backdropColor: # 針對雷達圖和極地圖的刻度標籤背景色 + light: "transparent" + dark: "transparent" + # Note - Bootstrap Callout note: # Note tag style values: diff --git a/layout/includes/third-party/math/chartjs.pug b/layout/includes/third-party/math/chartjs.pug new file mode 100644 index 0000000..4440c1f --- /dev/null +++ b/layout/includes/third-party/math/chartjs.pug @@ -0,0 +1,79 @@ +script. + (() => { + const $chartjs = document.querySelectorAll('#article-container .chartjs-wrap') + if ($chartjs.length === 0) return + + const applyThemeDefaultsConfig = (theme) => { + if (theme === 'dark-mode') { + Chart.defaults.color = "!{theme.chartjs.color.dark}" + Chart.defaults.borderColor = "!{theme.chartjs.borderColor.dark}" + Chart.defaults.scale.ticks.backdropColor = "!{theme.chartjs.scale.ticks.backdropColor.dark}" + } else { + Chart.defaults.color = "!{theme.chartjs.color.light}" + Chart.defaults.borderColor = "!{theme.chartjs.borderColor.light}" + Chart.defaults.scale.ticks.backdropColor = "!{theme.chartjs.scale.ticks.backdropColor.light}" + } + } + + // 递归遍历 config 对象,自动根据当前主题选择双模式配色方案 + const applyThemeConfig = (obj, theme) => { + if (typeof obj !== 'object' || obj === null) return + + Object.keys(obj).forEach(key => { + const value = obj[key] + // 如果属性是对象,并且有与主题相关的选项,进行应用 + if (typeof value === 'object' && value !== null) { + if (value[theme]) { + obj[key] = value[theme] // 应用当前主题的值 + } else { + // 递归处理子对象 + applyThemeConfig(value, theme) + } + } + }) + } + + const runChartJS = () => { + window.loadChartJS = true + + Array.from($chartjs).forEach((item, index) => { + const chartSrc = item.firstElementChild + const chartID = item.getAttribute('data-chartjs-id') || ('chartjs-' + index) // 使用自定义 ID 或默认 ID + const existingCanvas = document.getElementById(chartID) + + // 如果已经存在 canvas,先将其移除,避免重复渲染 + if (existingCanvas) { + existingCanvas.remove() + } + + const chartDefinition = chartSrc.textContent + const canvas = document.createElement('canvas') + canvas.id = chartID + chartSrc.insertAdjacentElement('afterend', canvas) + + const ctx = document.getElementById(chartID).getContext('2d') + + const config = JSON.parse(chartDefinition) + + // 根据当前主题选择相应的配色方案 + const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark-mode' : 'light-mode' + + // 设置默认样式(默认初始化) + applyThemeDefaultsConfig(theme) + + // 自动遍历 config,应用双模式配色方案 + applyThemeConfig(config, theme) + + new Chart(ctx, config) + }) + } + + const loadChartJS = () => { + window.loadChartJS ? runChartJS() : btf.getScript('!{url_for(theme.asset.chartjs)}').then(runChartJS) + } + + // 监听主题切换 + btf.addGlobalFn('themeChange', runChartJS, 'chartjs') + + window.pjax ? loadChartJS() : document.addEventListener('DOMContentLoaded', loadChartJS) + })() diff --git a/layout/includes/third-party/math/index.pug b/layout/includes/third-party/math/index.pug index 068a78f..217eb72 100644 --- a/layout/includes/third-party/math/index.pug +++ b/layout/includes/third-party/math/index.pug @@ -8,4 +8,7 @@ case theme.math.use include ./katex.pug if theme.mermaid.enable - include ./mermaid.pug \ No newline at end of file + include ./mermaid.pug + +if theme.chartjs.enable + include ./chartjs.pug \ No newline at end of file diff --git a/plugins.yml b/plugins.yml index 9b4bd9a..d509c5b 100644 --- a/plugins.yml +++ b/plugins.yml @@ -134,6 +134,10 @@ mermaid: name: mermaid file: dist/mermaid.min.js version: 11.2.1 +chartjs: + name: chart.js + file: dist/chart.umd.min.js + version: 4.4.4 meting_js: name: butterfly-extsrc file: metingjs/dist/Meting.min.js diff --git a/scripts/events/merge_config.js b/scripts/events/merge_config.js index 07e8bce..19c5547 100644 --- a/scripts/events/merge_config.js +++ b/scripts/events/merge_config.js @@ -512,6 +512,25 @@ hexo.extend.filter.register('before_generate', () => { dark: 'dark' } }, + chartjs: { + enable: false, + color: { + light: "rgba(0, 0, 0, 0.8)", + dark: "rgba(255, 255, 255, 0.8)" + }, + borderColor: { + light: "rgba(0, 0, 0, 0.1)", + dark: "rgba(255, 255, 255, 0.2)" + }, + scale: { + ticks: { + backdropColor: { + light: "transparent", + dark: "transparent" + } + } + } + }, note: { style: 'flat', icons: true, diff --git a/scripts/tag/chartjs.js b/scripts/tag/chartjs.js new file mode 100644 index 0000000..736dfb4 --- /dev/null +++ b/scripts/tag/chartjs.js @@ -0,0 +1,39 @@ +/** + * Butterfly + * chartjs + * https://www.chartjs.org/ + * Author: SeaYJ + */ + +'use strict' + +const { escapeHTML } = require('hexo-util') + +const chartjs = (args, content) => { + const chartBlock = /\n([\w\W\s\S]*?)/g + const descBlock = /\n([\w\W\s\S]*?)/g + const id = args[0] + const descMatches = [] + let chartConfig + let descDOM = '' + let match + + !content && hexo.log.warn('chartjs has no content!') + + if ((match = chartBlock.exec(content)) !== null) { + chartConfig = match[1] + } + + while ((match = descBlock.exec(content)) !== null) { + descMatches.push(match[1]) + } + + for (let i = 0; i < descMatches.length; i++) { + let descContent = hexo.render.renderSync({ text: descMatches[i], engine: 'markdown' }).trim() + descDOM += (descContent ? `
${descContent}
` : '') + } + + return `
${descDOM}
` +} + +hexo.extend.tag.register('chartjs', chartjs, { ends: true }) diff --git a/source/css/_layout/third-party.styl b/source/css/_layout/third-party.styl index 36806a5..00c86a5 100644 --- a/source/css/_layout/third-party.styl +++ b/source/css/_layout/third-party.styl @@ -75,6 +75,10 @@ if hexo-config('mermaid.enable') pre > code.mermaid display: none +.chartjs-wrap + margin: 0 0 20px + text-align: center + .utterances, .fb-comments iframe width: 100% !important