65 lines
2.0 KiB
JavaScript
65 lines
2.0 KiB
JavaScript
/* 主题系统:账户独立主题(dark/light/auto)。
|
||
*
|
||
* - 后端 GET /api/theme 返回 {mode, effective};auto 已解析为 dark/light
|
||
* (按用户经纬度+时区计算日出日落)。
|
||
* - 应用方式:html 元素加 .theme-light class(CSS 变量覆盖浅色)。
|
||
* - 切换动画:切换瞬间给 html 加 .theme-anim,触发全局 CSS transition
|
||
* 平滑渐变,平时无全局 transition(不影响交互反馈)。
|
||
* - auto 模式每 5 分钟轮询一次(日落日出边界切换);显示端还会在收到
|
||
* SSE theme_changed 时立即刷新。
|
||
*/
|
||
(function (global) {
|
||
'use strict';
|
||
|
||
var current = 'dark';
|
||
var pollTimer = null;
|
||
|
||
function apply(effective) {
|
||
if (!effective) return;
|
||
if (effective === current) return;
|
||
current = effective;
|
||
var root = document.documentElement;
|
||
root.classList.add('theme-anim');
|
||
root.classList.toggle('theme-light', effective === 'light');
|
||
// 过渡结束后移除动画 class,恢复常规交互反馈
|
||
setTimeout(function () { root.classList.remove('theme-anim'); }, 600);
|
||
}
|
||
|
||
function fetchTheme(cb) {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open('GET', '/api/theme');
|
||
xhr.onload = function () {
|
||
if (xhr.status === 200) {
|
||
var d = {};
|
||
try { d = JSON.parse(xhr.responseText || '{}'); } catch (e) {}
|
||
if (d.effective) apply(d.effective);
|
||
if (cb) cb(d);
|
||
}
|
||
};
|
||
xhr.onerror = function () { /* 网络失败保持当前主题 */ };
|
||
xhr.send();
|
||
}
|
||
|
||
function initTheme() {
|
||
fetchTheme();
|
||
if (!pollTimer) {
|
||
// auto 模式日落/日出切换:每 5 分钟刷新一次
|
||
pollTimer = setInterval(fetchTheme, 5 * 60 * 1000);
|
||
}
|
||
}
|
||
|
||
global.ThemeBridge = {
|
||
apply: apply,
|
||
fetch: fetchTheme,
|
||
init: initTheme,
|
||
getCurrent: function () { return current; }
|
||
};
|
||
|
||
// 页面加载即应用主题
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initTheme);
|
||
} else {
|
||
initTheme();
|
||
}
|
||
})(window);
|