feat(me): 添加无摄像头权限时的直接进入功能
- 新增直接进入按钮和倒计时提示界面 - 实现5秒后自动进入档案馆逻辑 - 添加摄像头权限状态管理和重试机制 - 支持点击按钮立即跳过摄像头授权 - 更新多语言字典中的相关提示文本 - 调整粒子爆炸效果参数提升视觉体验 - 优化手势识别逻辑仅在摄像头启用时运行 - 添加重新申请摄像头权限的交互入口
This commit is contained in:
250
me.html
250
me.html
@@ -104,6 +104,15 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.direct-enter-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-enter-btn:hover {
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: var(--loader-bg);
|
||||||
|
}
|
||||||
|
|
||||||
/* === 2. 叙事文本层 (DOM覆盖) === */
|
/* === 2. 叙事文本层 (DOM覆盖) === */
|
||||||
#narrative-layer {
|
#narrative-layer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -358,6 +367,10 @@
|
|||||||
<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="perm-hint" id="perm-guide">⚠ 请允许摄像头权限以开启手势交互</div>
|
||||||
|
<div class="direct-enter-container" id="direct-enter-container" style="margin-top: 20px; opacity: 0; transition: opacity 1s;">
|
||||||
|
<div class="direct-enter-hint" id="direct-enter-hint" style="font-size: 14px; margin-bottom: 15px;">3秒后自动进入</div>
|
||||||
|
<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>
|
||||||
|
|
||||||
<!-- 2. 叙事文本层 (DOM) -->
|
<!-- 2. 叙事文本层 (DOM) -->
|
||||||
@@ -412,6 +425,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'),
|
permHint: document.getElementById('perm-guide'),
|
||||||
|
directEnterContainer: document.getElementById('direct-enter-container'),
|
||||||
|
directEnterHint: document.getElementById('direct-enter-hint'),
|
||||||
|
directEnterBtn: document.getElementById('direct-enter-btn'),
|
||||||
|
|
||||||
// 叙事层相关元素
|
// 叙事层相关元素
|
||||||
narrativeLayer: document.getElementById('narrative-layer'),
|
narrativeLayer: document.getElementById('narrative-layer'),
|
||||||
@@ -473,6 +489,14 @@
|
|||||||
lang: getStoredLanguage()
|
lang: getStoredLanguage()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 摄像头权限状态管理
|
||||||
|
const CAMERA_STATE = {
|
||||||
|
enabled: false, // 摄像头是否已启用
|
||||||
|
permissionDenied: false, // 用户是否拒绝了权限
|
||||||
|
autoEnterTimeout: null, // 自动进入计时器
|
||||||
|
retryTimeout: null // 重试计时器
|
||||||
|
};
|
||||||
|
|
||||||
// 应用主题
|
// 应用主题
|
||||||
document.documentElement.setAttribute('data-theme', ENV.theme);
|
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.themeDisplay, `THEME: ${ENV.lang.toUpperCase() === 'en' ? ENV.theme.toUpperCase() : ENV.theme === 'day' ? '白天' : '黑夜'}`);
|
||||||
@@ -491,7 +515,12 @@
|
|||||||
hints: {
|
hints: {
|
||||||
main: "双手合十 · 解锁档案",
|
main: "双手合十 · 解锁档案",
|
||||||
sub: "单手·引力牵引|双手·力场排斥",
|
sub: "单手·引力牵引|双手·力场排斥",
|
||||||
unlocking: "正在识别..."
|
unlocking: "正在识别...",
|
||||||
|
directEnterHint: "5秒后自动进入",
|
||||||
|
directEnterBtn: "立即进入",
|
||||||
|
cameraDisabledMainHint: "点击进入档案馆",
|
||||||
|
cameraDisabledSubHint: "[ 再次申请摄像头权限 ]",
|
||||||
|
cameraRetryTimeout: "摄像头授权超时,5秒后取消"
|
||||||
},
|
},
|
||||||
slides: [
|
slides: [
|
||||||
{t: "初心", s: "在这喧嚣世界中,依然相信纯粹的力量"},
|
{t: "初心", s: "在这喧嚣世界中,依然相信纯粹的力量"},
|
||||||
@@ -509,7 +538,12 @@
|
|||||||
hints: {
|
hints: {
|
||||||
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",
|
||||||
|
directEnterBtn: "Enter Now",
|
||||||
|
cameraDisabledMainHint: "Click to Enter Archive",
|
||||||
|
cameraDisabledSubHint: "[ Request Camera Access Again ]",
|
||||||
|
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"},
|
||||||
@@ -524,6 +558,16 @@
|
|||||||
|
|
||||||
const CONTENT = DICTIONARY[ENV.lang];
|
const CONTENT = DICTIONARY[ENV.lang];
|
||||||
|
|
||||||
|
// 设置直接进入按钮的文本
|
||||||
|
safeUpdateText(DOM_CACHE.directEnterBtn, CONTENT.hints.directEnterBtn);
|
||||||
|
safeUpdateText(DOM_CACHE.directEnterHint, CONTENT.hints.directEnterHint);
|
||||||
|
|
||||||
|
// 直接进入按钮事件处理
|
||||||
|
DOM_CACHE.directEnterBtn.addEventListener('click', () => {
|
||||||
|
clearTimeout(CAMERA_STATE.autoEnterTimeout);
|
||||||
|
enterWithoutCamera();
|
||||||
|
});
|
||||||
|
|
||||||
// 循环播放加载文案
|
// 循环播放加载文案
|
||||||
let loadIdx = 0;
|
let loadIdx = 0;
|
||||||
const maxIdx = CONTENT.load.length - 1;
|
const maxIdx = CONTENT.load.length - 1;
|
||||||
@@ -536,9 +580,19 @@
|
|||||||
loadIdx++;
|
loadIdx++;
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
// 权限超时提示
|
// 权限超时提示和自动进入功能
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!APP_STATE.isLoaded) safeClass(DOM_CACHE.permHint, 'add', 'show');
|
if (!APP_STATE.isLoaded) {
|
||||||
|
safeClass(DOM_CACHE.permHint, 'add', 'show');
|
||||||
|
|
||||||
|
// 显示直接进入选项
|
||||||
|
DOM_CACHE.directEnterContainer.style.opacity = '1';
|
||||||
|
|
||||||
|
// 3秒后自动进入
|
||||||
|
CAMERA_STATE.autoEnterTimeout = setTimeout(() => {
|
||||||
|
enterWithoutCamera();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
@@ -808,9 +862,41 @@
|
|||||||
*/
|
*/
|
||||||
let narrativeTimer = null;
|
let narrativeTimer = null;
|
||||||
|
|
||||||
|
// 无摄像头进入档案馆
|
||||||
|
function enterWithoutCamera() {
|
||||||
|
if (APP_STATE.mode === 'UNLOCKED') return;
|
||||||
|
APP_STATE.mode = 'UNLOCKED';
|
||||||
|
CAMERA_STATE.enabled = false;
|
||||||
|
|
||||||
|
// 隐藏加载屏幕
|
||||||
|
const loader = DOM_CACHE.startScreen;
|
||||||
|
loader.style.opacity = 0;
|
||||||
|
DOM_CACHE.uiLayer.style.opacity = 1;
|
||||||
|
setTimeout(() => loader.style.display = 'none', 1000);
|
||||||
|
|
||||||
|
// 更新UI提示
|
||||||
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.cameraDisabledMainHint);
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.cameraDisabledSubHint);
|
||||||
|
|
||||||
|
// 显示退出按钮
|
||||||
|
safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
|
||||||
|
|
||||||
|
// 粒子爆炸效果
|
||||||
|
explode(300);
|
||||||
|
|
||||||
|
startNarrative();
|
||||||
|
}
|
||||||
|
|
||||||
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'); // 隐藏主提示 (CSS需支持或直接display)
|
||||||
@@ -819,7 +905,7 @@
|
|||||||
safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
|
safeClass(DOM_CACHE.exitBtn, 'add', 'visible');
|
||||||
|
|
||||||
// 粒子爆炸效果
|
// 粒子爆炸效果
|
||||||
explode(100);
|
explode(300);
|
||||||
|
|
||||||
startNarrative();
|
startNarrative();
|
||||||
}
|
}
|
||||||
@@ -828,13 +914,23 @@
|
|||||||
APP_STATE.mode = 'LOCKED';
|
APP_STATE.mode = 'LOCKED';
|
||||||
APP_STATE.exitCooldownUntil = Date.now() + 2000;
|
APP_STATE.exitCooldownUntil = Date.now() + 2000;
|
||||||
|
|
||||||
DOM_CACHE.mainHint.style.display = 'block';
|
// 根据摄像头状态更新UI
|
||||||
DOM_CACHE.subHint.style.display = 'block';
|
if (CAMERA_STATE.enabled) {
|
||||||
|
DOM_CACHE.mainHint.style.display = 'block';
|
||||||
|
DOM_CACHE.subHint.style.display = 'block';
|
||||||
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
|
safeUpdateText(DOM_CACHE.subHint, CONTENT.hints.sub);
|
||||||
|
} else {
|
||||||
|
DOM_CACHE.mainHint.style.display = 'block';
|
||||||
|
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;
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
|
||||||
clearTimeout(narrativeTimer);
|
clearTimeout(narrativeTimer);
|
||||||
explode(50);
|
explode(50);
|
||||||
}
|
}
|
||||||
@@ -897,68 +993,98 @@
|
|||||||
// Loader logic
|
// Loader logic
|
||||||
if (!APP_STATE.isLoaded) {
|
if (!APP_STATE.isLoaded) {
|
||||||
APP_STATE.isLoaded = true;
|
APP_STATE.isLoaded = true;
|
||||||
const loader = DOM_CACHE.startScreen;
|
CAMERA_STATE.enabled = true;
|
||||||
loader.style.opacity = 0;
|
|
||||||
DOM_CACHE.uiLayer.style.opacity = 1;
|
// 清除自动进入定时器
|
||||||
setTimeout(() => loader.style.display = 'none', 1000);
|
clearTimeout(CAMERA_STATE.autoEnterTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const landmarks = results.multiHandLandmarks;
|
// 只有在摄像头启用时才处理手势
|
||||||
const sysStatus = DOM_CACHE.sysStatus;
|
if (CAMERA_STATE.enabled) {
|
||||||
|
const landmarks = results.multiHandLandmarks;
|
||||||
|
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(sysStatus, `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';
|
||||||
|
|
||||||
// 坐标处理 (镜像)
|
// 坐标处理 (镜像)
|
||||||
const process = (lm) => ({
|
const process = (lm) => ({
|
||||||
x: ((1.0 - lm.x) * 2 - 1) * 800,
|
x: ((1.0 - lm.x) * 2 - 1) * 800,
|
||||||
y: -(lm.y * 2 - 1 - 0.2) * 600
|
y: -(lm.y * 2 - 1 - 0.2) * 600
|
||||||
});
|
});
|
||||||
|
|
||||||
const p1 = process(landmarks[0][9]);
|
const p1 = process(landmarks[0][9]);
|
||||||
APP_STATE.handL.x += (p1.x - APP_STATE.handL.x) * 0.25;
|
APP_STATE.handL.x += (p1.x - APP_STATE.handL.x) * 0.25;
|
||||||
APP_STATE.handL.y += (p1.y - APP_STATE.handL.y) * 0.25;
|
APP_STATE.handL.y += (p1.y - APP_STATE.handL.y) * 0.25;
|
||||||
|
|
||||||
if (landmarks.length > 1) {
|
if (landmarks.length > 1) {
|
||||||
const p2 = process(landmarks[1][9]);
|
const p2 = process(landmarks[1][9]);
|
||||||
APP_STATE.handR.x += (p2.x - APP_STATE.handR.x) * 0.25;
|
APP_STATE.handR.x += (p2.x - APP_STATE.handR.x) * 0.25;
|
||||||
APP_STATE.handR.y += (p2.y - APP_STATE.handR.y) * 0.25;
|
APP_STATE.handR.y += (p2.y - APP_STATE.handR.y) * 0.25;
|
||||||
}
|
|
||||||
|
|
||||||
// 合十检测
|
|
||||||
if (landmarks.length === 2 && APP_STATE.mode === 'LOCKED' && Date.now() > APP_STATE.exitCooldownUntil) {
|
|
||||||
const w1 = landmarks[0][0], w2 = landmarks[1][0];
|
|
||||||
const i1 = landmarks[0][8], i2 = landmarks[1][8];
|
|
||||||
const m1 = landmarks[0][12], m2 = landmarks[1][12];
|
|
||||||
const dW = Math.hypot(w1.x - w2.x, w1.y - w2.y);
|
|
||||||
const dI = Math.hypot(i1.x - i2.x, i1.y - i2.y);
|
|
||||||
const dM = Math.hypot(m1.x - m2.x, m1.y - m2.y);
|
|
||||||
const okW = dW < 0.28;
|
|
||||||
const okI = dI < 0.20;
|
|
||||||
const okM = dM < 0.20;
|
|
||||||
const ok = ((okW ? 1 : 0) + (okI ? 1 : 0) + (okM ? 1 : 0)) >= 2;
|
|
||||||
if (ok) {
|
|
||||||
APP_STATE.namasteStableFrames++;
|
|
||||||
} else {
|
|
||||||
APP_STATE.namasteStableFrames = Math.max(0, APP_STATE.namasteStableFrames - 1);
|
|
||||||
}
|
}
|
||||||
const targetFrames = 20;
|
|
||||||
const pct = Math.min(100, Math.round(APP_STATE.namasteStableFrames / targetFrames * 100));
|
// 合十检测
|
||||||
if (pct > 0) safeUpdateText(DOM_CACHE.mainHint, `${CONTENT.hints.unlocking} ${pct}%`);
|
if (landmarks.length === 2 && APP_STATE.mode === 'LOCKED' && Date.now() > APP_STATE.exitCooldownUntil) {
|
||||||
if (APP_STATE.namasteStableFrames >= targetFrames) enterArchive();
|
const w1 = landmarks[0][0], w2 = landmarks[1][0];
|
||||||
if (!ok && APP_STATE.namasteStableFrames === 0) safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
const i1 = landmarks[0][8], i2 = landmarks[1][8];
|
||||||
|
const m1 = landmarks[0][12], m2 = landmarks[1][12];
|
||||||
|
const dW = Math.hypot(w1.x - w2.x, w1.y - w2.y);
|
||||||
|
const dI = Math.hypot(i1.x - i2.x, i1.y - i2.y);
|
||||||
|
const dM = Math.hypot(m1.x - m2.x, m1.y - m2.y);
|
||||||
|
const okW = dW < 0.28;
|
||||||
|
const okI = dI < 0.20;
|
||||||
|
const okM = dM < 0.20;
|
||||||
|
const ok = ((okW ? 1 : 0) + (okI ? 1 : 0) + (okM ? 1 : 0)) >= 2;
|
||||||
|
if (ok) {
|
||||||
|
APP_STATE.namasteStableFrames++;
|
||||||
|
} else {
|
||||||
|
APP_STATE.namasteStableFrames = Math.max(0, APP_STATE.namasteStableFrames - 1);
|
||||||
|
}
|
||||||
|
const targetFrames = 20;
|
||||||
|
const pct = Math.min(100, Math.round(APP_STATE.namasteStableFrames / targetFrames * 100));
|
||||||
|
if (pct > 0) safeUpdateText(DOM_CACHE.mainHint, `${CONTENT.hints.unlocking} ${pct}%`);
|
||||||
|
if (APP_STATE.namasteStableFrames >= targetFrames) enterArchive();
|
||||||
|
if (!ok && APP_STATE.namasteStableFrames === 0) safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
APP_STATE.handCount = 0;
|
||||||
|
safeUpdateText(sysStatus, 'SEARCHING...');
|
||||||
|
if (sysStatus) sysStatus.style.color = 'inherit';
|
||||||
|
APP_STATE.unlockProgress = 0;
|
||||||
|
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
APP_STATE.handCount = 0;
|
|
||||||
safeUpdateText(sysStatus, 'SEARCHING...');
|
|
||||||
if (sysStatus) sysStatus.style.color = 'inherit';
|
|
||||||
APP_STATE.unlockProgress = 0;
|
|
||||||
safeUpdateText(DOM_CACHE.mainHint, CONTENT.hints.main);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DOM_CACHE.subHint.addEventListener('click', () => {
|
||||||
|
// 只有在无摄像头模式下才允许重新申请权限
|
||||||
|
if (!CAMERA_STATE.enabled && APP_STATE.mode === 'UNLOCKED') {
|
||||||
|
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 () => {
|
||||||
@@ -966,7 +1092,11 @@
|
|||||||
},
|
},
|
||||||
width: 640, height: 480
|
width: 640, height: 480
|
||||||
});
|
});
|
||||||
cameraUtils.start();
|
|
||||||
|
// 初始启动摄像头
|
||||||
|
cameraUtils.start().catch(err => {
|
||||||
|
console.log("Initial camera access denied:", err);
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
|
|||||||
Reference in New Issue
Block a user