feat: add reducemotion config option to optimize performance

- Add `reduceMotion` option in theme config to improve performance
- Update Button component to support reduced animations
- Modify GSAP animation logic to adapt to the new configuration
- Update documentation to reflect the new feature
This commit is contained in:
radishzzz 2025-05-19 14:54:59 +01:00
parent 120ebbbb3f
commit 4d247cfb93
12 changed files with 77 additions and 42 deletions

View file

@ -10,6 +10,7 @@ interface Props {
} }
const { light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color const { light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
const reduceMotion = themeConfig.global.reduceMotion
const { supportedLangs } = Astro.props const { supportedLangs } = Astro.props
const currentPath = Astro.url.pathname const currentPath = Astro.url.pathname
@ -61,7 +62,10 @@ const nextUrl = useSupportedLangs
</div> </div>
<!-- Theme Toggle Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> --> <!-- Theme Toggle Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<script is:inline define:vars={{ lightMode, darkMode }}> <script
is:inline
define:vars={{ lightMode, darkMode, reduceMotion }}
>
// Update theme // Update theme
function updateTheme() { function updateTheme() {
// Toggle website theme // Toggle website theme
@ -82,13 +86,18 @@ function updateTheme() {
// Bind click event to the button // Bind click event to the button
function setupThemeToggle() { function setupThemeToggle() {
// Add reduce-motion class if enabled in config
if (reduceMotion) {
document.documentElement.classList.add('reduce-motion')
}
// Locate theme toggle button // Locate theme toggle button
const themeToggleButton = document.getElementById('theme-toggle-button') const themeToggleButton = document.getElementById('theme-toggle-button')
// Add click listener to the button // Add click listener to the button
if (themeToggleButton) { if (themeToggleButton) {
themeToggleButton.addEventListener('click', () => { themeToggleButton.addEventListener('click', () => {
// If browser doesn't support View Transitions API, update theme directly // If reduceMotion is enabled or browser doesn't support View Transitions API, update theme directly
if (!document.startViewTransition) { if (reduceMotion || !document.startViewTransition) {
updateTheme() updateTheme()
return return
} }

View file

@ -2,13 +2,22 @@
import { gsap } from 'gsap' import { gsap } from 'gsap'
function setupPostPageAnimation() { function setupPostPageAnimation() {
const allElements = Array.from(document.querySelectorAll('#gsap-post-page-content > *, #gsap-post-page-tags, #waline')) // Post Content + Tags + Comments
const postContent = document.getElementById('gsap-post-page-content')
const postContentChildren = postContent ? Array.from(postContent.children) : []
const tagsElement = document.getElementById('gsap-post-page-tags')
const walineElement = document.getElementById('waline')
const allElements = [...postContentChildren, tagsElement, walineElement].filter(Boolean)
// TOC + Date + Back Button + TOC Icon
const tocList = document.getElementById('toc-list') const tocList = document.getElementById('toc-list')
const tocListChildren = tocList ? Array.from(tocList.children) : [] const tocListChildren = tocList ? Array.from(tocList.children) : []
const dateElement = document.getElementById('gsap-post-page-date') const dateElement = document.getElementById('gsap-post-page-date')
const backButton = document.getElementById('back-button') const backButton = document.getElementById('back-button')
const tocIcon = document.getElementById('toc-icon') const tocIcon = document.getElementById('toc-icon')
const tocContainer = document.getElementById('toc-container') const tocContainer = document.getElementById('toc-container')
// Screen Size Check
const isLargeScreen = window.matchMedia('(min-width: 1024px)').matches const isLargeScreen = window.matchMedia('(min-width: 1024px)').matches
const isSmallScreen = window.matchMedia('(max-width: 1535px)').matches const isSmallScreen = window.matchMedia('(max-width: 1535px)').matches
@ -81,8 +90,8 @@ function setupPostPageAnimation() {
} }
else { else {
// Post Content + Tags + Comments // Post Content + Tags + Comments
// First 7 elements // First 5 elements
gsap.from(allElements.slice(0, 7), { gsap.from(allElements.slice(0, 5), {
opacity: 0, opacity: 0,
y: '3rem', y: '3rem',
duration: 0.5, duration: 0.5,
@ -90,16 +99,16 @@ function setupPostPageAnimation() {
ease: 'power2.out', ease: 'power2.out',
stagger: 0.05, stagger: 0.05,
}) })
// Rest elements as the 8 element // Rest elements as the 6 element
if (allElements.length > 7) { // if (allElements.length > 5) {
gsap.from(allElements.slice(7), { // gsap.from(allElements.slice(7), {
opacity: 0, // opacity: 0,
y: '3rem', // y: '3rem',
duration: 0.5, // duration: 0.5,
delay: 0.2 + 0.05 * 5, // delay: 0.2 + 0.05 * 5,
ease: 'power2.out', // ease: 'power2.out',
}) // })
} // }
} }
// Mobile Animation (for screens smaller than 1536px) // Mobile Animation (for screens smaller than 1536px)

View file

@ -58,10 +58,12 @@ export const themeConfig: ThemeConfig = {
fontStyle: 'sans', // sans, serif fontStyle: 'sans', // sans, serif
// date format for posts // date format for posts
dateFormat: 'YYYY-MM-DD', // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY dateFormat: 'YYYY-MM-DD', // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
// enable KaTeX for mathematical formulas rendering
katex: true, // true, false
// enable table of contents for all posts by default // enable table of contents for all posts by default
toc: true, // true, false toc: true, // true, false
// enable KaTeX for mathematical formulas rendering
katex: true, // true, false
// reduce animations and transitions to improve performance
reduceMotion: false, // true, false
}, },
// GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END // GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
@ -143,7 +145,7 @@ export const themeConfig: ThemeConfig = {
{ {
name: 'Email', name: 'Email',
url: 'email@radishzz.cc', url: 'email@radishzz.cc',
} },
// { // {
// name: 'X', // name: 'X',
// url: 'https://x.com/radishzz_', // url: 'https://x.com/radishzz_',

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// enable KaTeX for mathematical formulas rendering
katex: true // true, false
// enable table of contents for all posts by default // enable table of contents for all posts by default
toc: true // true, false toc: true // true, false
// enable KaTeX for mathematical formulas rendering
katex: true // true, false
// reduce animations and transitions to improve performance
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ Pins the article to the top. The higher the number, the higher the priority of t
#### toc #### toc
Generate table of contents. Shows h2 to h4 headings. Uses the global `global.toc` configuration by default, but can be overridden individually in each article. Generate table of contents. Shows h2 to h4 headings. Determined by the global configuration `global.toc` by default, but can be overridden individually in each article.
#### lang #### lang

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// habilitar KaTeX para renderizar fórmulas matemáticas
katex: true // true, false
// habilitar tabla de contenidos para todos los artículos por defecto // habilitar tabla de contenidos para todos los artículos por defecto
toc: true // true, false toc: true // true, false
// habilitar KaTeX para renderizar fórmulas matemáticas
katex: true // true, false
// reducir animaciones y transiciones para mejorar el rendimiento
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ Fija el artículo en la parte superior. Cuanto mayor sea el número, mayor será
#### toc #### toc
Genera tabla de contenidos. Muestra encabezados de h2 a h4. Utiliza la configuración global `global.toc` por defecto, pero puede ser modificada individualmente en cada artículo. Genera tabla de contenidos. Muestra encabezados de h2 a h4. Determinado por la configuración global `global.toc` por defecto, pero puede ser modificada individualmente en cada artículo.
#### lang #### lang

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 数式表示のためのKaTeXを有効化
katex: true // true, false
// デフォルトですべての記事に目次を表示 // デフォルトですべての記事に目次を表示
toc: true // true, false toc: true // true, false
// 数式表示のためのKaTeXを有効化
katex: true // true, false
// アニメーションと遷移効果を減らしてパフォーマンスを向上させる
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ abbrlink: theme-guide
#### toc #### toc
目次を生成するかどうか。h2からh4までの見出しを表示します。デフォルトではグローバル設定 `global.toc` を使用しますが、記事ごとに個別に設定することも可能です。 目次を生成するかどうか。h2からh4までの見出しを表示します。デフォルトではグローバル設定項目 `global.toc` によって決定されますが、記事ごとに個別に設定して上書きすることもできます。
#### lang #### lang

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// включить KaTeX для отображения математических формул
katex: true // true, false
// включить оглавление для всех статей по умолчанию // включить оглавление для всех статей по умолчанию
toc: true // true, false toc: true // true, false
// включить KaTeX для отображения математических формул
katex: true // true, false
// уменьшить анимации и переходы для повышения производительности
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ abbrlink: theme-guide
#### toc #### toc
Генерировать оглавление. Показывает заголовки от h2 до h4. По умолчанию использует глобальный параметр `global.toc`, но может быть изменен индивидуально в каждой статье. Генерировать оглавление. Показывает заголовки от h2 до h4. По умолчанию определяется глобальным параметром `global.toc`, но может быть изменен индивидуально в каждой статье.
#### lang #### lang

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 啟用 KaTeX 數學公式渲染
katex: true // true, false
// 預設為所有文章開啟目錄 // 預設為所有文章開啟目錄
toc: true // true, false toc: true // true, false
// 啟用 KaTeX 數學公式渲染
katex: true // true, false
// 減少動畫和過渡效果以提高性能
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ abbrlink: theme-guide
#### toc #### toc
是否生成目錄。顯示 h2 至 h4 標題。預設為全域配置 `global.toc` 的選項,可在文章中單獨設定以覆蓋全域配置。 是否生成目錄。顯示 h2 至 h4 標題。預設由全域配置項 `global.toc` 決定,可在文章中單獨設定以覆蓋全域配置。
#### lang #### lang

View file

@ -84,10 +84,12 @@ global: {
// 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025 // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 202513 Apr 2025
// YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY // YYYY-MM-DD, MM-DD-YYYY, DD-MM-YYYY, MONTH DAY YYYY, DAY MONTH YYYY
dateFormat: 'YYYY-MM-DD' dateFormat: 'YYYY-MM-DD'
// 启用 KaTeX 数学公式渲染
katex: true // true, false
// 默认为所有文章开启目录 // 默认为所有文章开启目录
toc: true // true, false toc: true // true, false
// 启用 KaTeX 数学公式渲染
katex: true // true, false
// 减少动画和过渡效果以提高性能
reduceMotion: false // true, false
} }
``` ```
@ -317,7 +319,7 @@ abbrlink: theme-guide
#### toc #### toc
是否生成目录。显示 h2 至 h4 标题。默认为全局配置 `global.toc` 的选项,可在文章中单独设置以覆盖全局配置。 是否生成目录。显示 h2 至 h4 标题。默认由全局配置项 `global.toc` 决定,可在文章中单独设置以覆盖全局配置。
#### lang #### lang

View file

@ -25,6 +25,7 @@ interface Props {
const { postTitle, postDescription, postSlug, supportedLangs = [] } = Astro.props const { postTitle, postDescription, postSlug, supportedLangs = [] } = Astro.props
const { isPost } = getPageInfo(Astro.url.pathname) const { isPost } = getPageInfo(Astro.url.pathname)
const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans' const fontStyle = themeConfig.global.fontStyle === 'serif' ? 'font-serif' : 'font-sans'
const showAnimation = !themeConfig.global.reduceMotion
const MarginBottom = isPost && themeConfig.comment?.enabled const MarginBottom = isPost && themeConfig.comment?.enabled
? 'mb-10' // Post page with comments ? 'mb-10' // Post page with comments
: 'mb-12' // Other pages without comments : 'mb-12' // Other pages without comments
@ -48,11 +49,9 @@ const MarginBottom = isPost && themeConfig.comment?.enabled
</main> </main>
<Footer /> <Footer />
</div> </div>
<GsapAnimation /> {showAnimation && <GsapAnimation />}
<Button supportedLangs={supportedLangs} /> <Button supportedLangs={supportedLangs} />
<GithubCard /> <GithubCard />
<PhotoSwipe /> <PhotoSwipe />
<!-- Fix bottom space being cut off before page transition on mobile -->
<div class="fixed bottom-0 w-full"></div>
</body> </body>
</html> </html>

View file

@ -26,9 +26,12 @@ html[data-theme-changing] [data-disable-theme-transition] {
view-transition-name: none !important; view-transition-name: none !important;
} }
/* Fallback animation when view-transition-name is not supported */ /* Fallback transition for browsers not supporting view-transitions or when reduceMotion enabled */
@supports not (view-transition-name: none) { @supports not (view-transition-name: none) {
html { html {
--at-apply: 'transition-colors duration-300 ease-out'; --at-apply: 'transition-colors duration-300 ease-out';
} }
} }
html.reduce-motion {
--at-apply: 'transition-colors duration-300 ease-out';
}

View file

@ -33,8 +33,9 @@ export interface ThemeConfig {
moreLocales: typeof supportedLangs[number][] moreLocales: typeof supportedLangs[number][]
fontStyle: 'sans' | 'serif' fontStyle: 'sans' | 'serif'
dateFormat: 'YYYY-MM-DD' | 'MM-DD-YYYY' | 'DD-MM-YYYY' | 'MONTH DAY YYYY' | 'DAY MONTH YYYY' dateFormat: 'YYYY-MM-DD' | 'MM-DD-YYYY' | 'DD-MM-YYYY' | 'MONTH DAY YYYY' | 'DAY MONTH YYYY'
katex: boolean
toc: boolean toc: boolean
katex: boolean
reduceMotion: boolean
} }
comment: { comment: {