feat(ui): 重构进入界面与交互模式按钮
- 重新设计进入界面的按钮和倒计时提示样式 - 新增交互模式按钮,支持动态显示和隐藏 - 修改默认进入方式为动画模式而非叙事模式 - 调整摄像头权限申请逻辑,改为用户主动点击触发 - 优化加载文案循环播放逻辑 - 更新多语言字典中的相关提示文本 - 改进页面加载完成后不自动启动摄像头的处理方式
This commit is contained in:
355
me.html
355
me.html
@@ -104,15 +104,81 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.direct-enter-container {
|
/* 进入按钮样式 */
|
||||||
|
.enter-container {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.direct-enter-btn:hover {
|
.enter-btn:hover {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
color: var(--loader-bg);
|
color: var(--loader-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 交互模式按钮 */
|
||||||
|
.interaction-mode-btn {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 40px;
|
||||||
|
right: 40px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: var(--loader-bg);
|
||||||
|
border: none;
|
||||||
|
border-radius: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 255, 255, 0.3);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
z-index: 30;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction-mode-btn.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction-mode-btn:hover {
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 255, 255, 0.5);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(0, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 10px rgba(0, 255, 255, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(0, 255, 255, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 黑夜模式下的特殊样式 */
|
||||||
|
[data-theme="night"] .interaction-mode-btn {
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: #000;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="night"] .interaction-mode-btn:hover {
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 白天模式下的特殊样式 */
|
||||||
|
[data-theme="day"] .interaction-mode-btn {
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4px 12px rgba(53, 103, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="day"] .interaction-mode-btn:hover {
|
||||||
|
box-shadow: 0 6px 16px rgba(53, 103, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
/* === 2. 叙事文本层 (DOM覆盖) === */
|
/* === 2. 叙事文本层 (DOM覆盖) === */
|
||||||
#narrative-layer {
|
#narrative-layer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -366,10 +432,9 @@
|
|||||||
<div class="logo-text">Honesty</div>
|
<div class="logo-text">Honesty</div>
|
||||||
<div class="loader-ring"></div>
|
<div class="loader-ring"></div>
|
||||||
<div class="status-text" id="loader-msg">正在唤醒灵感...</div>
|
<div class="status-text" id="loader-msg">正在唤醒灵感...</div>
|
||||||
<div class="perm-hint" id="perm-guide">⚠ 请允许摄像头权限以开启手势交互</div>
|
<div class="enter-container" id="enter-container" style="margin-top: 30px; text-align: center; opacity: 0; transition: opacity 1s;">
|
||||||
<div class="direct-enter-container" id="direct-enter-container" style="margin-top: 20px; opacity: 0; transition: opacity 1s;">
|
<div class="countdown-hint" id="countdown-hint" style="font-size: 14px; margin-bottom: 15px; color: var(--loader-text);">5秒后自动进入</div>
|
||||||
<div class="direct-enter-hint" id="direct-enter-hint" style="font-size: 14px; margin-bottom: 15px;">3秒后自动进入</div>
|
<button class="enter-btn" id="enter-btn" style="padding: 12px 24px; background: transparent; border: 1px solid var(--accent-color); color: var(--loader-text); border-radius: 30px; cursor: pointer; font-size: 14px; letter-spacing: 2px;">立即进入</button>
|
||||||
<button class="direct-enter-btn" id="direct-enter-btn" style="padding: 10px 20px; background: transparent; border: 1px solid var(--accent-color); color: var(--loader-text); border-radius: 20px; cursor: pointer; font-size: 12px;">立即进入</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -388,7 +453,7 @@
|
|||||||
|
|
||||||
<div class="center-stage">
|
<div class="center-stage">
|
||||||
<div class="main-hint" id="main-hint" onclick="enterArchive()"></div>
|
<div class="main-hint" id="main-hint" onclick="enterArchive()"></div>
|
||||||
<div class="sub-hint" id="sub-hint"></div>
|
<div class="sub-hint" id="sub-hint" onclick="requestCameraAccess()"></div>
|
||||||
<div class="exit-btn" id="exit-btn" onclick="exitArchive()">[ 退出动画 ]</div>
|
<div class="exit-btn" id="exit-btn" onclick="exitArchive()">[ 退出动画 ]</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -424,10 +489,9 @@
|
|||||||
// 加载屏幕相关元素
|
// 加载屏幕相关元素
|
||||||
startScreen: document.getElementById('start-screen'),
|
startScreen: document.getElementById('start-screen'),
|
||||||
loaderMsg: document.getElementById('loader-msg'),
|
loaderMsg: document.getElementById('loader-msg'),
|
||||||
permHint: document.getElementById('perm-guide'),
|
enterContainer: document.getElementById('enter-container'),
|
||||||
directEnterContainer: document.getElementById('direct-enter-container'),
|
countdownHint: document.getElementById('countdown-hint'),
|
||||||
directEnterHint: document.getElementById('direct-enter-hint'),
|
enterBtn: document.getElementById('enter-btn'),
|
||||||
directEnterBtn: document.getElementById('direct-enter-btn'),
|
|
||||||
|
|
||||||
// 叙事层相关元素
|
// 叙事层相关元素
|
||||||
narrativeLayer: document.getElementById('narrative-layer'),
|
narrativeLayer: document.getElementById('narrative-layer'),
|
||||||
@@ -443,6 +507,9 @@
|
|||||||
subHint: document.getElementById('sub-hint'),
|
subHint: document.getElementById('sub-hint'),
|
||||||
exitBtn: document.getElementById('exit-btn'),
|
exitBtn: document.getElementById('exit-btn'),
|
||||||
|
|
||||||
|
// 交互模式按钮
|
||||||
|
interactionModeBtn: null,
|
||||||
|
|
||||||
// 其他元素
|
// 其他元素
|
||||||
canvasContainer: document.getElementById('canvas-container'),
|
canvasContainer: document.getElementById('canvas-container'),
|
||||||
inputVideo: document.getElementById('input-video')
|
inputVideo: document.getElementById('input-video')
|
||||||
@@ -489,19 +556,6 @@
|
|||||||
lang: getStoredLanguage()
|
lang: getStoredLanguage()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 摄像头权限状态管理
|
|
||||||
const CAMERA_STATE = {
|
|
||||||
enabled: false, // 摄像头是否已启用
|
|
||||||
permissionDenied: false, // 用户是否拒绝了权限
|
|
||||||
autoEnterTimeout: null, // 自动进入计时器
|
|
||||||
retryTimeout: null // 重试计时器
|
|
||||||
};
|
|
||||||
|
|
||||||
// 应用主题
|
|
||||||
document.documentElement.setAttribute('data-theme', ENV.theme);
|
|
||||||
safeUpdateText(DOM_CACHE.themeDisplay, `THEME: ${ENV.lang.toUpperCase() === 'en' ? ENV.theme.toUpperCase() : ENV.theme === 'day' ? '白天' : '黑夜'}`);
|
|
||||||
safeUpdateText(DOM_CACHE.langDisplay, ENV.lang.toUpperCase() === 'CN' ? '中文' : 'English');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
* 3. 内容字典
|
* 3. 内容字典
|
||||||
@@ -516,11 +570,10 @@
|
|||||||
main: "双手合十 · 解锁档案",
|
main: "双手合十 · 解锁档案",
|
||||||
sub: "单手·引力牵引|双手·力场排斥",
|
sub: "单手·引力牵引|双手·力场排斥",
|
||||||
unlocking: "正在识别...",
|
unlocking: "正在识别...",
|
||||||
directEnterHint: "5秒后自动进入",
|
countdownHint: "5秒后自动进入",
|
||||||
directEnterBtn: "立即进入",
|
enterBtn: "立即进入",
|
||||||
cameraDisabledMainHint: "点击进入档案馆",
|
interactionModeBtn: "交互模式",
|
||||||
cameraDisabledSubHint: "[ 再次申请摄像头权限 ]",
|
cameraAccessInfo: "点击启用交互模式"
|
||||||
cameraRetryTimeout: "摄像头授权超时,5秒后取消"
|
|
||||||
},
|
},
|
||||||
slides: [
|
slides: [
|
||||||
{t: "初心", s: "在这喧嚣世界中,依然相信纯粹的力量"},
|
{t: "初心", s: "在这喧嚣世界中,依然相信纯粹的力量"},
|
||||||
@@ -539,11 +592,10 @@
|
|||||||
main: "Click or Namaste",
|
main: "Click or Namaste",
|
||||||
sub: "One Hand Drag · Two Hands Repel",
|
sub: "One Hand Drag · Two Hands Repel",
|
||||||
unlocking: "Identifying...",
|
unlocking: "Identifying...",
|
||||||
directEnterHint: "Auto enter in 5 seconds",
|
countdownHint: "Auto enter in 5 seconds",
|
||||||
directEnterBtn: "Enter Now",
|
enterBtn: "Enter Now",
|
||||||
cameraDisabledMainHint: "Click to Enter Archive",
|
interactionModeBtn: "Interaction Mode",
|
||||||
cameraDisabledSubHint: "[ Request Camera Access Again ]",
|
cameraAccessInfo: "Click to enable camera interaction"
|
||||||
cameraRetryTimeout: "Camera authorization timeout, canceling in 5 seconds"
|
|
||||||
},
|
},
|
||||||
slides: [
|
slides: [
|
||||||
{t: "Honesty", s: "In a noisy world, still believe in the power of simplicity"},
|
{t: "Honesty", s: "In a noisy world, still believe in the power of simplicity"},
|
||||||
@@ -558,45 +610,22 @@
|
|||||||
|
|
||||||
const CONTENT = DICTIONARY[ENV.lang];
|
const CONTENT = DICTIONARY[ENV.lang];
|
||||||
|
|
||||||
// 设置直接进入按钮的文本
|
// 设置进入按钮的文本
|
||||||
safeUpdateText(DOM_CACHE.directEnterBtn, CONTENT.hints.directEnterBtn);
|
safeUpdateText(DOM_CACHE.countdownHint, CONTENT.hints.countdownHint);
|
||||||
safeUpdateText(DOM_CACHE.directEnterHint, CONTENT.hints.directEnterHint);
|
safeUpdateText(DOM_CACHE.enterBtn, CONTENT.hints.enterBtn);
|
||||||
|
|
||||||
// 直接进入按钮事件处理
|
// 摄像头权限状态管理
|
||||||
DOM_CACHE.directEnterBtn.addEventListener('click', () => {
|
const CAMERA_STATE = {
|
||||||
clearTimeout(CAMERA_STATE.autoEnterTimeout);
|
enabled: false, // 摄像头是否已启用
|
||||||
enterWithoutCamera();
|
permissionDenied: false, // 用户是否拒绝了权限
|
||||||
});
|
autoEnterTimeout: null, // 自动进入计时器
|
||||||
|
countdownInterval: null // 倒计时计时器
|
||||||
|
};
|
||||||
|
|
||||||
// 循环播放加载文案
|
// 应用主题
|
||||||
let loadIdx = 0;
|
document.documentElement.setAttribute('data-theme', ENV.theme);
|
||||||
const maxIdx = CONTENT.load.length - 1;
|
safeUpdateText(DOM_CACHE.themeDisplay, `THEME: ${ENV.lang.toUpperCase() === 'en' ? ENV.theme.toUpperCase() : ENV.theme === 'day' ? '白天' : '黑夜'}`);
|
||||||
const loadTimer = setInterval(() => {
|
safeUpdateText(DOM_CACHE.langDisplay, ENV.lang.toUpperCase() === 'CN' ? '中文' : 'English');
|
||||||
if (APP_STATE.isLoaded) {
|
|
||||||
clearInterval(loadTimer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
safeUpdateText(DOM_CACHE.loaderMsg, CONTENT.load[loadIdx > maxIdx ? maxIdx : loadIdx]);
|
|
||||||
loadIdx++;
|
|
||||||
}, 600);
|
|
||||||
|
|
||||||
// 权限超时提示和自动进入功能
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!APP_STATE.isLoaded) {
|
|
||||||
safeClass(DOM_CACHE.permHint, 'add', 'show');
|
|
||||||
|
|
||||||
// 显示直接进入选项
|
|
||||||
DOM_CACHE.directEnterContainer.style.opacity = '1';
|
|
||||||
|
|
||||||
// 3秒后自动进入动画模式
|
|
||||||
CAMERA_STATE.autoEnterTimeout = setTimeout(() => {
|
|
||||||
enterWithoutCamera();
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
|
||||||
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
@@ -857,16 +886,15 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
* 6. 逻辑控制 (叙事/退出) - 修复空指针问题
|
* 6. 逻辑控制 (动画/退出)
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
let narrativeTimer = null;
|
let narrativeTimer = null;
|
||||||
|
|
||||||
// 无摄像头进入动画模式(不是叙事模式)
|
// 进入动画模式
|
||||||
function enterWithoutCamera() {
|
function enterAnimationMode() {
|
||||||
if (APP_STATE.isLoaded) return;
|
if (APP_STATE.isLoaded) return;
|
||||||
APP_STATE.isLoaded = true;
|
APP_STATE.isLoaded = true;
|
||||||
CAMERA_STATE.enabled = false;
|
|
||||||
|
|
||||||
// 隐藏加载屏幕
|
// 隐藏加载屏幕
|
||||||
const loader = DOM_CACHE.startScreen;
|
const loader = DOM_CACHE.startScreen;
|
||||||
@@ -874,27 +902,75 @@
|
|||||||
DOM_CACHE.uiLayer.style.opacity = 1;
|
DOM_CACHE.uiLayer.style.opacity = 1;
|
||||||
setTimeout(() => loader.style.display = 'none', 1000);
|
setTimeout(() => loader.style.display = 'none', 1000);
|
||||||
|
|
||||||
// 更新UI提示
|
// 显示交互模式按钮(如果摄像头未启用)
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.cameraDisabledMainHint);
|
if (!CAMERA_STATE.enabled) {
|
||||||
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraDisabledSubHint);
|
showInteractionModeButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 清除加载文案定时器
|
// 显示交互模式按钮
|
||||||
clearInterval(loadTimer);
|
function showInteractionModeButton() {
|
||||||
|
// 创建交互模式按钮
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.className = 'interaction-mode-btn';
|
||||||
|
btn.id = 'interaction-mode-btn';
|
||||||
|
btn.textContent = CONTENT.hints.interactionModeBtn;
|
||||||
|
document.body.appendChild(btn);
|
||||||
|
|
||||||
|
// 缓存按钮引用
|
||||||
|
DOM_CACHE.interactionModeBtn = btn;
|
||||||
|
|
||||||
|
// 显示按钮
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.classList.add('visible');
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// 添加点击事件
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
requestCameraAccess();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求摄像头权限
|
||||||
|
function requestCameraAccess() {
|
||||||
|
if (CAMERA_STATE.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 隐藏交互模式按钮
|
||||||
|
if (DOM_CACHE.interactionModeBtn) {
|
||||||
|
DOM_CACHE.interactionModeBtn.classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新提示文本
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraAccessInfo);
|
||||||
|
|
||||||
|
// 尝试启动摄像头
|
||||||
|
cameraUtils.start().then(() => {
|
||||||
|
// 成功启动,更新状态
|
||||||
|
CAMERA_STATE.enabled = true;
|
||||||
|
CAMERA_STATE.permissionDenied = false;
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (DOM_CACHE.interactionModeBtn && DOM_CACHE.interactionModeBtn.parentNode) {
|
||||||
|
DOM_CACHE.interactionModeBtn.parentNode.removeChild(DOM_CACHE.interactionModeBtn);
|
||||||
|
DOM_CACHE.interactionModeBtn = null;
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}).catch(err => {
|
||||||
|
// 权限被拒绝
|
||||||
|
console.log("Camera access error:", err);
|
||||||
|
CAMERA_STATE.permissionDenied = true;
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraAccessInfo);
|
||||||
|
DOM_CACHE.interactionModeBtn.classList.add('visible');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.enterArchive = function () {
|
window.enterArchive = function () {
|
||||||
if (APP_STATE.mode === 'UNLOCKED') return;
|
if (APP_STATE.mode === 'UNLOCKED') return;
|
||||||
APP_STATE.mode = 'UNLOCKED';
|
APP_STATE.mode = 'UNLOCKED';
|
||||||
CAMERA_STATE.enabled = true;
|
|
||||||
|
|
||||||
// 隐藏加载屏幕
|
|
||||||
const loader = DOM_CACHE.startScreen;
|
|
||||||
loader.style.opacity = 0;
|
|
||||||
DOM_CACHE.uiLayer.style.opacity = 1;
|
|
||||||
setTimeout(() => loader.style.display = 'none', 1000);
|
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
safeClass(DOM_CACHE.mainHint, 'add', 'hidden'); // 隐藏主提示 (CSS需支持或直接display)
|
safeClass(DOM_CACHE.mainHint, 'add', 'hidden');
|
||||||
DOM_CACHE.mainHint.style.display = 'none';
|
DOM_CACHE.mainHint.style.display = 'none';
|
||||||
DOM_CACHE.subHint.style.display = 'none';
|
DOM_CACHE.subHint.style.display = 'none';
|
||||||
safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
|
safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
|
||||||
@@ -909,25 +985,27 @@
|
|||||||
APP_STATE.mode = 'LOCKED';
|
APP_STATE.mode = 'LOCKED';
|
||||||
APP_STATE.exitCooldownUntil = Date.now() + 2000;
|
APP_STATE.exitCooldownUntil = Date.now() + 2000;
|
||||||
|
|
||||||
// 根据摄像头状态更新UI
|
|
||||||
if (CAMERA_STATE.enabled) {
|
|
||||||
DOM_CACHE.mainHint.style.display = 'block';
|
DOM_CACHE.mainHint.style.display = 'block';
|
||||||
DOM_CACHE.subHint.style.display = 'block';
|
DOM_CACHE.subHint.style.display = 'block';
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
|
if (CAMERA_STATE.enabled) {
|
||||||
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
||||||
} else {
|
} else {
|
||||||
DOM_CACHE.mainHint.style.display = 'block';
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraAccessInfo);
|
||||||
DOM_CACHE.subHint.style.display = 'block';
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.cameraDisabledMainHint);
|
|
||||||
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraDisabledSubHint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
safeClass(DOM_CACHE.exitBtn, 'remove', 'visible');
|
safeClass(DOM_CACHE.exitBtn, 'remove', 'visible');
|
||||||
safeClass(DOM_CACHE.narrativeLayer, 'remove', 'show-text');
|
safeClass(DOM_CACHE.narrativeLayer, 'remove', 'show-text');
|
||||||
|
|
||||||
APP_STATE.unlockProgress = 0;
|
APP_STATE.unlockProgress = 0;
|
||||||
clearTimeout(narrativeTimer);
|
clearTimeout(narrativeTimer);
|
||||||
explode(50);
|
explode(50);
|
||||||
|
|
||||||
|
// 退出后重新显示交互模式按钮(如果之前已启用摄像头)
|
||||||
|
if (CAMERA_STATE.enabled) {
|
||||||
|
showInteractionModeButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function explode(f) {
|
function explode(f) {
|
||||||
@@ -989,9 +1067,6 @@
|
|||||||
if (!APP_STATE.isLoaded) {
|
if (!APP_STATE.isLoaded) {
|
||||||
APP_STATE.isLoaded = true;
|
APP_STATE.isLoaded = true;
|
||||||
CAMERA_STATE.enabled = true;
|
CAMERA_STATE.enabled = true;
|
||||||
|
|
||||||
// 清除自动进入定时器
|
|
||||||
clearTimeout(CAMERA_STATE.autoEnterTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有在摄像头启用时才处理手势
|
// 只有在摄像头启用时才处理手势
|
||||||
@@ -1053,44 +1128,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
DOM_CACHE.subHint.addEventListener('click', () => {
|
|
||||||
// 只有在无摄像头模式下才允许重新申请权限
|
|
||||||
if (!CAMERA_STATE.enabled && APP_STATE.isLoaded) {
|
|
||||||
requestCameraAccess();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 请求摄像头权限
|
|
||||||
function requestCameraAccess() {
|
|
||||||
// 更新提示文本
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.cameraRetryTimeout);
|
|
||||||
|
|
||||||
// 尝试启动摄像头
|
|
||||||
cameraUtils.start().then(() => {
|
|
||||||
// 成功启动,隐藏提示
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
|
||||||
}).catch(err => {
|
|
||||||
console.log("Camera access error:", err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 5秒后取消
|
|
||||||
CAMERA_STATE.retryTimeout = setTimeout(() => {
|
|
||||||
cameraUtils.stop();
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.cameraDisabledMainHint);
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
const videoElement = DOM_CACHE.inputVideo;
|
const videoElement = DOM_CACHE.inputVideo;
|
||||||
const cameraUtils = new Camera(videoElement, {
|
const cameraUtils = new Camera(videoElement, {
|
||||||
onFrame: async () => {
|
onFrame: async () => {
|
||||||
|
try {
|
||||||
await hands.send({image: videoElement});
|
await hands.send({image: videoElement});
|
||||||
|
} catch (err) {
|
||||||
|
// 捕获并隐藏技术性错误信息
|
||||||
|
if (err.name !== 'NotAllowedError' && err.message.indexOf('Permission dismissed') === -1) {
|
||||||
|
console.log("Camera processing error:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
width: 640, height: 480
|
width: 640, height: 480
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始启动摄像头
|
// 页面加载完成后不自动启动摄像头
|
||||||
cameraUtils.start().catch(err => {
|
window.addEventListener('load', () => {
|
||||||
console.log("Initial camera access denied:", err);
|
console.log("Page loaded, camera not initialized");
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
@@ -1101,6 +1156,52 @@
|
|||||||
material.uniforms.scale.value = window.innerHeight / 2;
|
material.uniforms.scale.value = window.innerHeight / 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 循环播放加载文案
|
||||||
|
let loadIdx = 0;
|
||||||
|
const maxIdx = CONTENT.load.length - 1;
|
||||||
|
const loadTimer = setInterval(() => {
|
||||||
|
if (APP_STATE.isLoaded) {
|
||||||
|
clearInterval(loadTimer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
safeUpdateText(DOM_CACHE.loaderMsg, CONTENT.load[loadIdx > maxIdx ? maxIdx : loadIdx]);
|
||||||
|
loadIdx++;
|
||||||
|
}, 600);
|
||||||
|
|
||||||
|
// 资源加载完成后显示进入按钮
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!APP_STATE.isLoaded) {
|
||||||
|
// 显示进入选项
|
||||||
|
DOM_CACHE.enterContainer.style.opacity = '1';
|
||||||
|
|
||||||
|
// 开始5秒倒计时
|
||||||
|
let countdown = 5;
|
||||||
|
safeUpdateText(DOM_CACHE.countdownHint, `${countdown}秒后自动进入`);
|
||||||
|
|
||||||
|
CAMERA_STATE.countdownInterval = setInterval(() => {
|
||||||
|
countdown--;
|
||||||
|
if (countdown > 0) {
|
||||||
|
safeUpdateText(DOM_CACHE.countdownHint, `${countdown}秒后自动进入`);
|
||||||
|
} else {
|
||||||
|
clearInterval(CAMERA_STATE.countdownInterval);
|
||||||
|
enterAnimationMode();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
// 进入按钮事件处理
|
||||||
|
DOM_CACHE.enterBtn.addEventListener('click', () => {
|
||||||
|
clearInterval(CAMERA_STATE.countdownInterval);
|
||||||
|
enterAnimationMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
|
if (CAMERA_STATE.enabled) {
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
||||||
|
} else {
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraAccessInfo);
|
||||||
|
}
|
||||||
animate();
|
animate();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user