跳到主要内容

2 篇博文 含有标签「Docusaurus」

查看所有标签

小恐龙艺廊包🐔

· 阅读需 2 分钟

我把我现在的 Docusaurus 项目,简化成了一个画廊模板包,叫 小恐龙艺廊包

小恐龙艺廊包!你的docusaurus宝宝辅食😊!

你应该有一个自己的数字根据地,快行动起来吧!

其实就是给视觉艺术家们用的一个小工具。里面有画廊页面、字体切换,Mermaid 图表也提前开好了——当然,当然MDX 还能插音乐,不过不是这次的重点。

不过考虑到宝宝向用户,我只保留了画廊页面,其他功能没动。用熟了喜欢了,你大可以自己改。现在有 AI 帮忙,改前端真的超级方便,别怕。

快行动起来吧。😄


功能

  • 🖼️ 画廊页面(我做的!)/sketch 路径下自动展示分类图片
  • 🔤 字体切换(这个也是我做的!):右上角一键切换系统 / 像素字体
  • 📊 Mermaid 图表:已预置,支持流程图、时序图等
  • 🎵 MDX 支持(本来就支持啦!):可插音乐、视频、互动组件

(项目包内附详细使用说明,安装完node.js后,点击项目包的本地渲染.bat全在里面👀。)

使用方式

  1. 解压项目包
  2. 安装 Node.js(我把安装包都准备好了!就在项目根目录里,双击安装就行,一路“下一步”)
  3. 运行依赖安装.bat,linux/mac用户在终端运行npm install
  4. 把图片放进 static/sketch/你的分类名/
  5. 双击 图片抓取.bat 生成索引
  6. 双击 本地渲染.bat 运行项目

下载

📦 下载小恐龙艺廊包

EX.

©️版权- Docusaurus 采用的是 MIT 许可证,随便用啦。

我的增加部分(如配置、样式、脚本及文档)则是随便用,不用问我。
当然,如果你觉得有用,愿意提一下「小恐龙艺廊包 · 顾游Gize_Ku」就更好了,我会很感激的。😊


有问题可以联系我 📮 gizeku@outlook.com
也欢迎常来 小花园🌻 看看。

追加新功能字体切换②

· 阅读需 7 分钟

复盘一下技术路径,方便自己以后查看,也方便大家在其他静态站生成器里面重写,如果是docusaurus则可以照搬。

设计的思路图在最下面,放前面一大块,我自己看得不爽👀。1

说是要兼容两个设备,好像很烦。其实在docusaurus里面挺好实现的,
涉及修改的文件路径目录

项目根目录/
├── src/
│ ├── css/
│ │ └── custom.css ← 修改:定义 @font-face、:root 变量、[data-theme-font] 规则
│ └── theme/
│ └── Root.js ← 新增/修改:注入导航栏按钮,处理所有交互逻辑
├── static/
│ └── fonts/ ← 放你自己需要的字体就行👍
│ ├── ZLabsPixel_12px_M_CN.ttf.woff2 ← 新增:像素字体文件
│ └── SmileySans-Oblique.otf.woff2 ← 新增:得意黑字体文件
└── docusaurus.config.js ← 无需修改(保持原样)
`custom.css`需要追加的内容
/* ============================================================
🎯 字体切换功能 · 可配置模板
============================================================ */

/* ---------- 1. 定义你的字体 ---------- */
/* 将你的字体文件放在 static/fonts/ 目录下,然后替换下方的名称和路径 */

@font-face {
font-family: '你的字体A名称'; /* ← 改这里 */
src: url('/fonts/你的字体A文件名.woff2') format('woff2'); /* ← 改这里 */
font-weight: normal;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: '你的字体B名称'; /* ← 改这里 */
src: url('/fonts/你的字体B文件名.woff2') format('woff2'); /* ← 改这里 */
font-weight: 400;
font-style: normal;
font-display: swap;
}

/* ---------- 2. 基础变量(默认系统字体) ---------- */
:root {
--ifm-font-family-base: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* ---------- 3. 切换方案(与 data-theme-font 配合) ---------- */
[data-theme-font='字体A-key'] { /* ← key 可自定义,与 JS 中对应 */
--ifm-font-family-base: '你的字体A名称', system-ui, sans-serif;
}

[data-theme-font='字体B-key'] { /* ← key 可自定义,与 JS 中对应 */
--ifm-font-family-base: '你的字体B名称', system-ui, sans-serif;
}

📄 字体切换 — Root.js
import React, { useEffect } from 'react';

function FontSwitcherButton() {
useEffect(() => {
// 等待导航栏渲染完成后,插入按钮
const insertButton = () => {
const navbar = document.querySelector('.navbar__items--right');
if (navbar && !document.querySelector('.font-switcher-btn')) {
// 读取当前字体偏好
const saved = localStorage.getItem('font-preference') || 'system';
const isCustom = saved !== 'system';

const btn = document.createElement('button');
btn.className = 'font-switcher-btn';
btn.style.cssText = `
background: none;
border: none;
cursor: pointer;
padding: 4px 6px;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.2s;
border-radius: 4px;
color: var(--ifm-navbar-link-color);
`;
btn.onmouseenter = () => btn.style.opacity = '1';
btn.onmouseleave = () => btn.style.opacity = '0.7';

// 渲染 SVG 滑块(isCustom: true=绿色右侧,false=灰色左侧)
const renderSVG = (custom) => {
return `
<svg width="34" height="20" viewBox="0 0 34 20" style="display:block;">
<rect
x="0" y="0" width="34" height="20" rx="10"
fill="${custom ? '#4CAF50' : '#ccc'}"
style="transition: fill 0.25s ease;"
/>
<circle
cx="${custom ? '25' : '9'}"
cy="10"
r="7"
fill="white"
style="transition: cx 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: 0 1px 4px rgba(0,0,0,0.15);"
/>
</svg>
`;
};

btn.innerHTML = renderSVG(isCustom);
btn.title = isCustom ? '切换到系统字体' : '切换到自定义字体';

// 交互逻辑:左键切换 + 右键/长按/双击弹出菜单
let pressTimer = null;
let clickTimer = null;

// 动态获取字体列表
const getFontList = () => {
const fonts = [];
const seenKeys = new Set();
fonts.push({ key: 'system', label: '系统字体' });
seenKeys.add('system');

for (const sheet of document.styleSheets) {
try {
const rules = sheet.cssRules || sheet.rules;
if (!rules) continue;
for (const rule of rules) {
if (rule.selectorText && rule.selectorText.includes('[data-theme-font=')) {
const match = rule.selectorText.match(/\[data-theme-font="([^"]+)"\]/);
if (match && match[1]) {
const key = match[1];
if (seenKeys.has(key)) continue;
seenKeys.add(key);
const fontFamily = rule.style.getPropertyValue('--ifm-font-family-base');
let label = key;
if (fontFamily) {
const names = fontFamily.split(',').map(s => s.replace(/['"]/g, '').trim());
label = names[0] || key;
}
fonts.push({ key, label });
}
}
}
} catch (e) {
continue;
}
}
return fonts;
};

// 菜单功能
const showFontMenu = (x, y) => {
const existingMenu = document.querySelector('.font-switcher-menu');
if (existingMenu) existingMenu.remove();

const fonts = getFontList();
if (fonts.length === 0) {
fonts.push(
{ key: 'system', label: '系统字体' },
{ key: 'pixel', label: '像素字体' }
);
}

const menu = document.createElement('div');
menu.className = 'font-switcher-menu';
menu.style.cssText = `
position: fixed;
top: ${Math.min(y, window.innerHeight - 120)}px;
left: ${Math.min(x, window.innerWidth - 140)}px;
background: var(--ifm-background-surface-color, #fff);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
padding: 6px 0;
z-index: 9999;
min-width: 120px;
border: 1px solid var(--ifm-color-emphasis-200, #e0e0e0);
font-size: 0.9rem;
`;

const current = document.documentElement.getAttribute('data-theme-font') || 'system';

fonts.forEach((font) => {
const item = document.createElement('div');
item.textContent = font.label;
const isActive = font.key === current;
item.style.cssText = `
padding: 6px 16px;
cursor: pointer;
transition: background 0.15s;
color: var(--ifm-font-color-base, #333);
${isActive ? 'font-weight: bold; background: var(--ifm-color-emphasis-100, #f0f0f0);' : ''}
`;
item.onmouseenter = () => item.style.background = 'var(--ifm-color-emphasis-100, #f0f0f0)';
item.onmouseleave = () => {
if (!isActive) item.style.background = 'transparent';
else item.style.background = 'var(--ifm-color-emphasis-100, #f0f0f0)';
};
item.onclick = () => {
document.documentElement.setAttribute('data-theme-font', font.key);
localStorage.setItem('font-preference', font.key);
if (font.key !== 'system') {
localStorage.setItem('last-custom-font', font.key);
}
updateSliderUI(font.key !== 'system');
menu.remove();
};
menu.appendChild(item);
});

const closeMenu = (e) => {
if (!menu.contains(e.target)) {
menu.remove();
document.removeEventListener('click', closeMenu);
}
};
setTimeout(() => document.addEventListener('click', closeMenu), 10);

document.body.appendChild(menu);
};

// 更新滑块 UI
const updateSliderUI = (isCustom) => {
const svg = btn.querySelector('svg');
if (svg) {
const rect = svg.querySelector('rect');
const circle = svg.querySelector('circle');
if (rect) rect.setAttribute('fill', isCustom ? '#4CAF50' : '#ccc');
if (circle) circle.setAttribute('cx', isCustom ? '25' : '9');
}
btn.title = isCustom ? '切换到系统字体' : '切换到自定义字体';
};

// 1. 左键点击:快速切换
btn.onclick = (e) => {
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
return;
}

const current = document.documentElement.getAttribute('data-theme-font') || 'system';
const lastCustom = localStorage.getItem('last-custom-font') || 'pixel';
const next = current === 'system' ? lastCustom : 'system';
const nextIsCustom = next !== 'system';

document.documentElement.setAttribute('data-theme-font', next);
localStorage.setItem('font-preference', next);
updateSliderUI(nextIsCustom);
};

// 2. 右键点击:弹出菜单
btn.oncontextmenu = (e) => {
e.preventDefault();
showFontMenu(e.clientX, e.clientY);
};

// 3. 长按(≥500ms):弹出菜单
btn.onmousedown = (e) => {
if (e.button !== 0) return;
pressTimer = setTimeout(() => {
showFontMenu(e.clientX, e.clientY);
pressTimer = null;
}, 500);
};
btn.onmouseup = () => {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
btn.onmouseleave = () => {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};

// 4. 双击:弹出菜单
btn.ondblclick = (e) => {
e.preventDefault();
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
}
showFontMenu(e.clientX, e.clientY);
};

// 防双击的单击处理
btn.addEventListener('click', (e) => {
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
return;
}
clickTimer = setTimeout(() => {
clickTimer = null;
}, 300);
});

// 初始化 UI 状态
const savedState = localStorage.getItem('font-preference') || 'system';
updateSliderUI(savedState !== 'system');

// 插入到深色模式按钮之前
navbar.insertBefore(btn, navbar.lastElementChild);
}
};

// 初次插入
insertButton();

// 监听路由变化,重新插入(Docusaurus 是 SPA)
const observer = new MutationObserver(() => insertButton());
observer.observe(document.body, { childList: true, subtree: true });

return () => observer.disconnect();
}, []);

return null;
}

export default function Root({ children }) {
return (
<>
<FontSwitcherButton />
{children}
</>
);
}

Footnotes

  1. 没那么复杂啦