perf(me.html): 优化DOM访问以提升页面性能

- 引入DOM_CACHE对象缓存频繁访问的DOM元素
- 修改safeUpdateText和safeClass函数以接受DOM元素而非ID
- 替换所有document.getElementById调用为DOM_CACHE中的缓存引用
- 减少重复DOM查询,提高渲染和交互性能
This commit is contained in:
hehh
2025-12-04 18:27:47 +08:00
parent 5ced418d1a
commit 0f6504a46b

92
me.html
View File

@@ -406,15 +406,39 @@
APP_STATE.namasteStableFrames = 0; APP_STATE.namasteStableFrames = 0;
// 缓存频繁访问的DOM元素以提高性能
const DOM_CACHE = {
// 加载屏幕相关元素
startScreen: document.getElementById('start-screen'),
loaderMsg: document.getElementById('loader-msg'),
permHint: document.getElementById('perm-guide'),
// 叙事层相关元素
narrativeLayer: document.getElementById('narrative-layer'),
nTitle: document.getElementById('n-title'),
nSub: document.getElementById('n-sub'),
// UI层相关元素
uiLayer: document.getElementById('ui-layer'),
sysStatus: document.getElementById('sys-status'),
themeDisplay: document.getElementById('theme-display'),
langDisplay: document.getElementById('lang-display'),
mainHint: document.getElementById('main-hint'),
subHint: document.getElementById('sub-hint'),
exitBtn: document.getElementById('exit-btn'),
// 其他元素
canvasContainer: document.getElementById('canvas-container'),
inputVideo: document.getElementById('input-video')
};
// 安全DOM操作防止报错 // 安全DOM操作防止报错
function safeUpdateText(id, text) { function safeUpdateText(element, text) {
const el = document.getElementById(id); if (element) element.innerText = text;
if (el) el.innerText = text;
} }
function safeClass(id, method, className) { function safeClass(element, method, className) {
const el = document.getElementById(id); if (element) element.classList[method](className);
if (el) el.classList[method](className);
} }
/** /**
@@ -451,8 +475,8 @@
// 应用主题 // 应用主题
document.documentElement.setAttribute('data-theme', ENV.theme); document.documentElement.setAttribute('data-theme', ENV.theme);
safeUpdateText('theme-display', `THEME: ${ENV.lang.toUpperCase() === 'en' ? ENV.theme.toUpperCase() : ENV.theme === 'day' ? '白天' : '黑夜'}`); safeUpdateText(DOM_CACHE.themeDisplay, `THEME: ${ENV.lang.toUpperCase() === 'en' ? ENV.theme.toUpperCase() : ENV.theme === 'day' ? '白天' : '黑夜'}`);
safeUpdateText('lang-display', ENV.lang.toUpperCase() === 'CN' ? '中文' : 'English'); safeUpdateText(DOM_CACHE.langDisplay, ENV.lang.toUpperCase() === 'CN' ? '中文' : 'English');
/** /**
* ============================================================================ * ============================================================================
@@ -508,17 +532,17 @@
clearInterval(loadTimer); clearInterval(loadTimer);
return; return;
} }
safeUpdateText('loader-msg', CONTENT.load[loadIdx > maxIdx ? maxIdx : loadIdx]); safeUpdateText(DOM_CACHE.loaderMsg, CONTENT.load[loadIdx > maxIdx ? maxIdx : loadIdx]);
loadIdx++; loadIdx++;
}, 600); }, 600);
// 权限超时提示 // 权限超时提示
setTimeout(() => { setTimeout(() => {
if (!APP_STATE.isLoaded) safeClass('perm-hint', 'add', 'show'); if (!APP_STATE.isLoaded) safeClass(DOM_CACHE.permHint, 'add', 'show');
}, 5000); }, 5000);
safeUpdateText('main-hint', CONTENT.hints.main); safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
safeUpdateText('sub-hint', CONTENT.hints.sub); safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
/** /**
* ============================================================================ * ============================================================================
@@ -548,7 +572,7 @@
renderer.setSize(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setClearColor(0x000000, 0); renderer.setClearColor(0x000000, 0);
document.getElementById('canvas-container').appendChild(renderer.domElement); DOM_CACHE.canvasContainer.appendChild(renderer.domElement);
const composer = new THREE.EffectComposer(renderer); const composer = new THREE.EffectComposer(renderer);
composer.addPass(new THREE.RenderPass(scene, camera)); composer.addPass(new THREE.RenderPass(scene, camera));
@@ -789,10 +813,10 @@
APP_STATE.mode = 'UNLOCKED'; APP_STATE.mode = 'UNLOCKED';
// UI // UI
safeClass('main-hint', 'add', 'hidden'); // 隐藏主提示 (CSS需支持或直接display) safeClass(DOM_CACHE.mainHint, 'add', 'hidden'); // 隐藏主提示 (CSS需支持或直接display)
document.getElementById('main-hint').style.display = 'none'; DOM_CACHE.mainHint.style.display = 'none';
document.getElementById('sub-hint').style.display = 'none'; DOM_CACHE.subHint.style.display = 'none';
safeClass('exit-btn', 'add', 'visible'); safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
// 粒子爆炸效果 // 粒子爆炸效果
explode(100); explode(100);
@@ -804,13 +828,13 @@
APP_STATE.mode = 'LOCKED'; APP_STATE.mode = 'LOCKED';
APP_STATE.exitCooldownUntil = Date.now() + 2000; APP_STATE.exitCooldownUntil = Date.now() + 2000;
document.getElementById('main-hint').style.display = 'block'; DOM_CACHE.mainHint.style.display = 'block';
document.getElementById('sub-hint').style.display = 'block'; DOM_CACHE.subHint.style.display = 'block';
safeClass('exit-btn', 'remove', 'visible'); safeClass(DOM_CACHE.exitBtn, 'remove', 'visible');
safeClass('narrative-layer', 'remove', 'show-text'); safeClass(DOM_CACHE.narrativeLayer, 'remove', 'show-text');
APP_STATE.unlockProgress = 0; APP_STATE.unlockProgress = 0;
safeUpdateText('main-hint', CONTENT.hints.main); safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
clearTimeout(narrativeTimer); clearTimeout(narrativeTimer);
explode(50); explode(50);
} }
@@ -825,9 +849,9 @@
function startNarrative() { function startNarrative() {
let idx = 0; let idx = 0;
const nTitle = document.getElementById('n-title'); const nTitle = DOM_CACHE.nTitle;
const nSub = document.getElementById('n-sub'); const nSub = DOM_CACHE.nSub;
const layer = document.getElementById('narrative-layer'); const layer = DOM_CACHE.narrativeLayer;
// 必须检查元素是否存在 // 必须检查元素是否存在
if (!nTitle || !nSub || !layer) return; if (!nTitle || !nSub || !layer) return;
@@ -873,18 +897,18 @@
// Loader logic // Loader logic
if (!APP_STATE.isLoaded) { if (!APP_STATE.isLoaded) {
APP_STATE.isLoaded = true; APP_STATE.isLoaded = true;
const loader = document.getElementById('start-screen'); const loader = DOM_CACHE.startScreen;
loader.style.opacity = 0; loader.style.opacity = 0;
document.getElementById('ui-layer').style.opacity = 1; DOM_CACHE.uiLayer.style.opacity = 1;
setTimeout(() => loader.style.display = 'none', 1000); setTimeout(() => loader.style.display = 'none', 1000);
} }
const landmarks = results.multiHandLandmarks; const landmarks = results.multiHandLandmarks;
const sysStatus = document.getElementById('sys-status'); const sysStatus = DOM_CACHE.sysStatus;
if (landmarks && landmarks.length > 0) { if (landmarks && landmarks.length > 0) {
APP_STATE.handCount = landmarks.length; APP_STATE.handCount = landmarks.length;
safeUpdateText('sys-status', `LINKED (${APP_STATE.handCount})`); safeUpdateText(sysStatus, `LINKED (${APP_STATE.handCount})`);
if (sysStatus) sysStatus.style.color = ENV.theme === 'day' ? '#0044cc' : '#00ff00'; if (sysStatus) sysStatus.style.color = ENV.theme === 'day' ? '#0044cc' : '#00ff00';
// 坐标处理 (镜像) // 坐标处理 (镜像)
@@ -922,20 +946,20 @@
} }
const targetFrames = 20; const targetFrames = 20;
const pct = Math.min(100, Math.round(APP_STATE.namasteStableFrames / targetFrames * 100)); const pct = Math.min(100, Math.round(APP_STATE.namasteStableFrames / targetFrames * 100));
if (pct > 0) safeUpdateText('main-hint', `${CONTENT.hints.unlocking} ${pct}%`); if (pct > 0) safeUpdateText(DOM_CACHE.mainHint, `${CONTENT.hints.unlocking} ${pct}%`);
if (APP_STATE.namasteStableFrames >= targetFrames) enterArchive(); if (APP_STATE.namasteStableFrames >= targetFrames) enterArchive();
if (!ok && APP_STATE.namasteStableFrames === 0) safeUpdateText('main-hint', CONTENT.hints.main); if (!ok && APP_STATE.namasteStableFrames === 0) safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
} }
} else { } else {
APP_STATE.handCount = 0; APP_STATE.handCount = 0;
safeUpdateText('sys-status', 'SEARCHING...'); safeUpdateText(sysStatus, 'SEARCHING...');
if (sysStatus) sysStatus.style.color = 'inherit'; if (sysStatus) sysStatus.style.color = 'inherit';
APP_STATE.unlockProgress = 0; APP_STATE.unlockProgress = 0;
safeUpdateText('main-hint', CONTENT.hints.main); safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
} }
}); });
const videoElement = document.getElementById('input-video'); const videoElement = DOM_CACHE.inputVideo;
const cameraUtils = new Camera(videoElement, { const cameraUtils = new Camera(videoElement, {
onFrame: async () => { onFrame: async () => {
await hands.send({image: videoElement}); await hands.send({image: videoElement});