93914f54b0
Adjust default font size from 13px to 14px and modify blockquote styling Convert fixed pixel values to relative em units for better scalability Improve spacing and typography consistency across components
303 lines
7.8 KiB
Vue
303 lines
7.8 KiB
Vue
<script setup>
|
|
import DefaultTheme from 'vitepress/theme'
|
|
import { useData } from 'vitepress'
|
|
import TextType from './components/TextType.vue'
|
|
import GitHubStars from './components/GitHubStars.vue'
|
|
import { onMounted, ref, watch } from 'vue'
|
|
import { Setting } from '@element-plus/icons-vue'
|
|
|
|
const { frontmatter } = useData()
|
|
|
|
const homeTaglineTyping = {
|
|
typingSpeed: 45,
|
|
initialDelay: 0,
|
|
pauseDuration: 2500,
|
|
postDeletingDelay: 500,
|
|
deletingSpeed: 18
|
|
}
|
|
|
|
const FONT_SIZE_STORAGE_KEY = 'ev-doc-font-size'
|
|
const LINE_HEIGHT_STORAGE_KEY = 'ev-doc-line-height'
|
|
const MIN_FONT_SIZE = 12
|
|
const MAX_FONT_SIZE = 18
|
|
const DEFAULT_FONT_SIZE = 14
|
|
const MIN_LINE_HEIGHT = 1.25
|
|
const MAX_LINE_HEIGHT = 1.8
|
|
const DEFAULT_LINE_HEIGHT = 1.65
|
|
|
|
const fontSize = ref(DEFAULT_FONT_SIZE)
|
|
const lineHeight = ref(DEFAULT_LINE_HEIGHT)
|
|
const isHydrated = ref(false)
|
|
|
|
const clampFontSize = (value) => {
|
|
if (value === null || value === undefined || value === '') return DEFAULT_FONT_SIZE
|
|
const numeric = Number(value)
|
|
if (!Number.isFinite(numeric)) return DEFAULT_FONT_SIZE
|
|
return Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, numeric))
|
|
}
|
|
|
|
const clampLineHeight = (value) => {
|
|
if (value === null || value === undefined || value === '') return DEFAULT_LINE_HEIGHT
|
|
const numeric = Number(value)
|
|
if (!Number.isFinite(numeric)) return DEFAULT_LINE_HEIGHT
|
|
return Math.min(MAX_LINE_HEIGHT, Math.max(MIN_LINE_HEIGHT, numeric))
|
|
}
|
|
|
|
const applyFontSize = (size) => {
|
|
if (typeof document === 'undefined') return
|
|
document.documentElement.style.setProperty('--ev-doc-font-size', `${size}px`)
|
|
}
|
|
|
|
const applyLineHeight = (value) => {
|
|
if (typeof document === 'undefined') return
|
|
document.documentElement.style.setProperty('--ev-doc-line-height', String(value))
|
|
}
|
|
|
|
const decreaseFontSize = () => {
|
|
fontSize.value = clampFontSize(fontSize.value - 1)
|
|
}
|
|
|
|
const increaseFontSize = () => {
|
|
fontSize.value = clampFontSize(fontSize.value + 1)
|
|
}
|
|
|
|
const resetFontSize = () => {
|
|
fontSize.value = DEFAULT_FONT_SIZE
|
|
}
|
|
|
|
const resetLineHeight = () => {
|
|
lineHeight.value = DEFAULT_LINE_HEIGHT
|
|
}
|
|
|
|
onMounted(() => {
|
|
const saved = clampFontSize(localStorage.getItem(FONT_SIZE_STORAGE_KEY))
|
|
const savedLineHeight = clampLineHeight(localStorage.getItem(LINE_HEIGHT_STORAGE_KEY))
|
|
fontSize.value = saved
|
|
lineHeight.value = savedLineHeight
|
|
applyFontSize(saved)
|
|
applyLineHeight(savedLineHeight)
|
|
isHydrated.value = true
|
|
})
|
|
|
|
watch(fontSize, (next) => {
|
|
if (!isHydrated.value) return
|
|
const normalized = clampFontSize(next)
|
|
applyFontSize(normalized)
|
|
localStorage.setItem(FONT_SIZE_STORAGE_KEY, String(normalized))
|
|
})
|
|
|
|
watch(lineHeight, (next) => {
|
|
if (!isHydrated.value) return
|
|
const normalized = clampLineHeight(next)
|
|
applyLineHeight(normalized)
|
|
localStorage.setItem(LINE_HEIGHT_STORAGE_KEY, String(normalized))
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<DefaultTheme.Layout>
|
|
<template #nav-bar-content-after>
|
|
<GitHubStars />
|
|
<ClientOnly>
|
|
<el-popover placement="bottom-end" trigger="click" :width="260">
|
|
<template #reference>
|
|
<button
|
|
class="ev-fontsize-button"
|
|
type="button"
|
|
aria-label="阅读设置"
|
|
style="margin-left: 16px; padding: 0; width: 32px;"
|
|
>
|
|
<el-icon :size="16"><Setting /></el-icon>
|
|
</button>
|
|
</template>
|
|
<div class="ev-fontsize-panel">
|
|
<div class="ev-setting-group">
|
|
<div class="ev-setting-header">
|
|
<div class="ev-setting-title">字号</div>
|
|
<div class="ev-setting-value">{{ fontSize }}px</div>
|
|
</div>
|
|
<div class="ev-fontsize-actions">
|
|
<button
|
|
class="ev-fontsize-action"
|
|
type="button"
|
|
@click="decreaseFontSize"
|
|
>
|
|
A-
|
|
</button>
|
|
<button
|
|
class="ev-fontsize-action"
|
|
type="button"
|
|
@click="resetFontSize"
|
|
>
|
|
默认
|
|
</button>
|
|
<button
|
|
class="ev-fontsize-action"
|
|
type="button"
|
|
@click="increaseFontSize"
|
|
>
|
|
A+
|
|
</button>
|
|
</div>
|
|
<el-slider v-model="fontSize" :min="MIN_FONT_SIZE" :max="MAX_FONT_SIZE" :step="1" />
|
|
</div>
|
|
|
|
<div class="ev-setting-group">
|
|
<div class="ev-setting-header">
|
|
<div class="ev-setting-title">行距</div>
|
|
<div class="ev-setting-value">{{ lineHeight.toFixed(2) }}</div>
|
|
</div>
|
|
<div class="ev-fontsize-actions">
|
|
<button class="ev-fontsize-action" type="button" @click="resetLineHeight">
|
|
默认
|
|
</button>
|
|
<button
|
|
class="ev-fontsize-action"
|
|
type="button"
|
|
@click="lineHeight = clampLineHeight(lineHeight - 0.05)"
|
|
>
|
|
更紧
|
|
</button>
|
|
<button
|
|
class="ev-fontsize-action"
|
|
type="button"
|
|
@click="lineHeight = clampLineHeight(lineHeight + 0.05)"
|
|
>
|
|
更松
|
|
</button>
|
|
</div>
|
|
<el-slider
|
|
v-model="lineHeight"
|
|
:min="MIN_LINE_HEIGHT"
|
|
:max="MAX_LINE_HEIGHT"
|
|
:step="0.05"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</el-popover>
|
|
</ClientOnly>
|
|
</template>
|
|
<template #home-hero-info-after>
|
|
<div
|
|
v-if="frontmatter.layout === 'home' && frontmatter.hero?.tagline"
|
|
class="vp-typed-tagline"
|
|
>
|
|
<ClientOnly>
|
|
<TextType
|
|
:text="frontmatter.hero.tagline"
|
|
v-bind="homeTaglineTyping"
|
|
:loop="true"
|
|
/>
|
|
</ClientOnly>
|
|
</div>
|
|
</template>
|
|
</DefaultTheme.Layout>
|
|
</template>
|
|
|
|
<style>
|
|
/* 隐藏默认的 tagline,因为我们用打字机效果替代了它 */
|
|
.VPHomeHero .tagline {
|
|
display: none !important;
|
|
}
|
|
|
|
/* 调整打字机容器的样式,使其看起来像原来的 tagline */
|
|
.vp-typed-tagline {
|
|
padding-top: 8px;
|
|
line-height: 28px;
|
|
font-size: 18px;
|
|
font-weight: 500;
|
|
white-space: pre-wrap;
|
|
color: var(--vp-c-text-2);
|
|
min-height: 28px;
|
|
display: flex;
|
|
/* 确保左对齐 */
|
|
text-align: left;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.vp-typed-tagline {
|
|
line-height: 32px;
|
|
font-size: 20px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 960px) {
|
|
.vp-typed-tagline {
|
|
line-height: 36px;
|
|
font-size: 24px;
|
|
}
|
|
}
|
|
|
|
.ev-fontsize-button {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 32px;
|
|
min-width: 32px;
|
|
padding: 0 10px;
|
|
border: 1px solid var(--vp-c-divider);
|
|
border-radius: 999px;
|
|
background: var(--vp-c-bg-soft);
|
|
color: var(--vp-c-text-1);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ev-fontsize-button:hover {
|
|
border-color: var(--vp-c-brand);
|
|
color: var(--vp-c-brand);
|
|
}
|
|
|
|
.ev-fontsize-panel {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
|
|
.ev-setting-group {
|
|
display: grid;
|
|
gap: 8px;
|
|
}
|
|
|
|
.ev-setting-header {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.ev-setting-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--vp-c-text-1);
|
|
}
|
|
|
|
.ev-setting-value {
|
|
font-size: 12px;
|
|
color: var(--vp-c-text-2);
|
|
}
|
|
|
|
.ev-fontsize-actions {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 8px;
|
|
}
|
|
|
|
.ev-fontsize-action {
|
|
height: 32px;
|
|
border: 1px solid var(--vp-c-divider);
|
|
border-radius: 10px;
|
|
background: var(--vp-c-bg-soft);
|
|
color: var(--vp-c-text-1);
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ev-fontsize-action:hover {
|
|
border-color: var(--vp-c-brand);
|
|
color: var(--vp-c-brand);
|
|
}
|
|
</style>
|