管理后台可关全站动画,并细调代码折叠、平滑滚动、装饰入场;统一 lib/motion,尊重系统减少动态效果。 Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
/**
|
||
* 全站动画策略:系统「减少动态效果」优先;站点总开关/子开关可进一步关闭。
|
||
* 新增性能相关动画时: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;
|
||
}
|