feat(settings): 站点界面动画总开关与子开关
管理后台可关全站动画,并细调代码折叠、平滑滚动、装饰入场;统一 lib/motion,尊重系统减少动态效果。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
89
frontend/lib/motion.ts
Normal file
89
frontend/lib/motion.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 全站动画策略:系统「减少动态效果」优先;站点总开关/子开关可进一步关闭。
|
||||
* 新增性能相关动画时:CSS 挂 j13-reduce-*,JS 走 allowMotion / scrollBehavior。
|
||||
*/
|
||||
|
||||
export type MotionKind = "codeFold" | "smoothScroll" | "chrome";
|
||||
|
||||
export type MotionSiteConfig = {
|
||||
/** 界面动画总开关 */
|
||||
ui_animations: boolean;
|
||||
/** 代码折叠高度与跟滚 */
|
||||
anim_code_fold: boolean;
|
||||
/** 平滑滚动 */
|
||||
anim_smooth_scroll: boolean;
|
||||
/** 装饰入场:列表/Toast/点赞/脉冲等 */
|
||||
anim_chrome: boolean;
|
||||
};
|
||||
|
||||
export const DEFAULT_MOTION_CONFIG: MotionSiteConfig = {
|
||||
ui_animations: true,
|
||||
anim_code_fold: true,
|
||||
anim_smooth_scroll: true,
|
||||
anim_chrome: true,
|
||||
};
|
||||
|
||||
let siteConfig: MotionSiteConfig = { ...DEFAULT_MOTION_CONFIG };
|
||||
|
||||
export function getMotionSiteConfig(): MotionSiteConfig {
|
||||
return siteConfig;
|
||||
}
|
||||
|
||||
export function motionConfigFromSettings(s: {
|
||||
ui_animations?: boolean;
|
||||
anim_code_fold?: boolean;
|
||||
anim_smooth_scroll?: boolean;
|
||||
anim_chrome?: boolean;
|
||||
}): MotionSiteConfig {
|
||||
return {
|
||||
ui_animations: s.ui_animations !== false,
|
||||
anim_code_fold: s.anim_code_fold !== false,
|
||||
anim_smooth_scroll: s.anim_smooth_scroll !== false,
|
||||
anim_chrome: s.anim_chrome !== false,
|
||||
};
|
||||
}
|
||||
|
||||
export function osPrefersReducedMotion(): boolean {
|
||||
return (
|
||||
typeof window !== "undefined" &&
|
||||
window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||
);
|
||||
}
|
||||
|
||||
/** 是否允许指定类动画(OS reduce 或总开关关闭则一律否) */
|
||||
export function allowMotion(kind: MotionKind = "chrome"): boolean {
|
||||
if (osPrefersReducedMotion()) return false;
|
||||
if (!siteConfig.ui_animations) return false;
|
||||
switch (kind) {
|
||||
case "codeFold":
|
||||
return siteConfig.anim_code_fold;
|
||||
case "smoothScroll":
|
||||
return siteConfig.anim_smooth_scroll;
|
||||
case "chrome":
|
||||
return siteConfig.anim_chrome;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function scrollBehavior(): ScrollBehavior {
|
||||
return allowMotion("smoothScroll") ? "smooth" : "auto";
|
||||
}
|
||||
|
||||
/** 同步 documentElement class,供 CSS 与 SSR 初值一致 */
|
||||
export function syncMotionHtmlClasses(cfg: MotionSiteConfig = siteConfig): void {
|
||||
if (typeof document === "undefined") return;
|
||||
const root = document.documentElement;
|
||||
root.classList.toggle("j13-reduce-motion", !cfg.ui_animations);
|
||||
root.classList.toggle(
|
||||
"j13-reduce-chrome",
|
||||
cfg.ui_animations && !cfg.anim_chrome
|
||||
);
|
||||
}
|
||||
|
||||
/** 应用站点动画配置(SSR 初值 / WS 热更新) */
|
||||
export function applyMotionSiteConfig(cfg: MotionSiteConfig): MotionSiteConfig {
|
||||
siteConfig = motionConfigFromSettings(cfg);
|
||||
syncMotionHtmlClasses(siteConfig);
|
||||
return siteConfig;
|
||||
}
|
||||
Reference in New Issue
Block a user