Files
jiang13-bbs/frontend/lib/motion.ts
freefire 2b27703e6c feat(settings): 站点界面动画总开关与子开关
管理后台可关全站动画,并细调代码折叠、平滑滚动、装饰入场;统一 lib/motion,尊重系统减少动态效果。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 06:59:26 +08:00

90 lines
2.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 全站动画策略:系统「减少动态效果」优先;站点总开关/子开关可进一步关闭。
* 新增性能相关动画时: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;
}