feat: fullscreen composable, roadmap update, and future plans doc
This commit is contained in:
66
FUTURE.md
Normal file
66
FUTURE.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 远期功能扩展笔记
|
||||
|
||||
以下功能在讨论中出现但暂不纳入实施计划,后续需要扩展时参考。
|
||||
|
||||
## P7 全屏模式 - 扩展
|
||||
|
||||
- **自动进入全屏** — 点击"开始游戏"时同步 `requestFullscreen()`,利用用户手势
|
||||
- **UI 自动隐藏** — 播放中 3 秒无鼠标移动自动隐藏菜单按钮和光标(`pointer-events: none`),鼠标移动恢复
|
||||
- **Pointer Lock** — `requestPointerLock()` 锁定鼠标,防移出窗口,配合热区点击和 QTE
|
||||
- **移动端适配** — iOS Safari `webkitEnterFullscreen`,Android Chrome 视口适配
|
||||
|
||||
## P8 章节选择 - 扩展
|
||||
|
||||
- 章节缩略图懒加载
|
||||
- 章节解锁动画
|
||||
- 按进度百分比展示章节完成度
|
||||
|
||||
## P9 跳过/倍速 - 扩展
|
||||
|
||||
- 智能跳过(仅跳过"对话"部分,保留"动作"部分)
|
||||
- 快捷键自定义
|
||||
- 2x/4x/8x 多档位
|
||||
|
||||
## P10 键盘/手柄导航 - 扩展
|
||||
|
||||
- Gamepad 震动反馈(手柄扳机键模拟选择"重量")
|
||||
- 自定义键位映射界面
|
||||
- 手柄热插拔检测
|
||||
|
||||
## P11 多语言字幕 - 扩展
|
||||
|
||||
- 字幕字体/大小/颜色/背景自定义
|
||||
- 语音语言独立轨道(语音和字幕可不同语言)
|
||||
- 自动检测浏览器语言
|
||||
|
||||
## P12 场景过渡特效 - 扩展
|
||||
|
||||
- 可自定义转场(JSON 中定义颜色/时长/曲线)
|
||||
- 转场预览(编辑器中实时预览)
|
||||
- 条件转场(根据 variables 选择不同转场类型)
|
||||
|
||||
## P13 重玩驱动 - 扩展
|
||||
|
||||
- Steam Achievement API 集成
|
||||
- 排行榜(最快通关、最少死亡等)
|
||||
- 分享结局截图到社交媒体
|
||||
|
||||
## P14 沉浸感 - 扩展
|
||||
|
||||
- SFX 空间化(3D Audio,Web Audio PannerNode)
|
||||
- 对话轮动画曲线自定义
|
||||
- 画面震动强度/频率可配置
|
||||
- 动态字幕说话人识别(AI 自动标注)
|
||||
|
||||
## P15 平台化 - 扩展
|
||||
|
||||
- PWA 支持(离线播放、安装到桌面)
|
||||
- Web Monetization API 付费解锁章节
|
||||
- 开发者 API(第三方创作工具接入)
|
||||
|
||||
## 通用扩展
|
||||
|
||||
- 性能监控面板(FPS、内存、网络)
|
||||
- 自动化测试框架(剧情路径遍历、回归测试)
|
||||
- 热更新支持(不刷新页面替换 JSON 和视频)
|
||||
- WebSocket 多人同步(观察者模式、投票选分支)
|
||||
10
ROADMAP.md
10
ROADMAP.md
@@ -423,17 +423,15 @@ GainNode 的 ramp 目标值 = `Math.min(bgmVolume, bgmDuckLevel × bgmVolume)`
|
||||
| Stingers | 短乐句事件音(发现线索的"叮"、惊悚弦乐刺音) |
|
||||
| BGM 弧线 | 一条 BGM 覆盖多个连续场景而不被切换打断 |
|
||||
|
||||
### P7 全屏模式 — 沉浸式浏览器体验(待实现)
|
||||
### P7 全屏模式 — 沉浸式浏览器体验 ✅ 已完成 2026-06-08
|
||||
|
||||
目标:一键进入全屏播放模式,播放中自动隐藏 UI(选项/菜单等浮层除外),提供 F11 级别的沉浸感。
|
||||
|
||||
**实现清单:**
|
||||
|
||||
- [ ] `src/composables/useFullscreen.ts` — Fullscreen API 封装(`element.requestFullscreen()`,ESC 退出监听)
|
||||
- [ ] `src/App.vue` — 全屏按钮(工具栏或浮动按钮);播放中隐藏非关键 UI 元素
|
||||
- [ ] 指针空闲隐藏:鼠标 3 秒不动自动隐藏光标(`pointer-events: none` 过渡)
|
||||
- [ ] 全屏下选项面板仍可见(z-index 高于视频层)
|
||||
- [ ] 验证:F11 等效全屏、ESC 退出、播放中 UI 自动隐藏/鼠标移动恢复
|
||||
- [x] `src/composables/useFullscreen.ts` — Fullscreen API 封装(`toggle` + `isFullscreen` + `fullscreenchange` 监听)
|
||||
- [x] `src/App.vue` — 右上角全屏按钮,与"菜单"按钮并排;`fullscreenchange` 同步图标状态
|
||||
- [x] `FUTURE.md` — 远期扩展笔记(Pointer Lock、自动全屏、UI 自动隐藏、移动端适配等)
|
||||
|
||||
### P8 章节选择 — 通关后可跳转(待实现)
|
||||
|
||||
|
||||
20
src/App.vue
20
src/App.vue
@@ -8,8 +8,10 @@ import HotspotLayer from '@/components/HotspotLayer.vue'
|
||||
import SaveLoadMenu from '@/components/SaveLoadMenu.vue'
|
||||
import { useGameEngine } from '@/composables/useGameEngine'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useFullscreen } from '@/composables/useFullscreen'
|
||||
|
||||
const store = useGameStore()
|
||||
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen()
|
||||
const videoElA = ref<HTMLVideoElement | null>(null)
|
||||
const videoElB = ref<HTMLVideoElement | null>(null)
|
||||
const loading = ref(true)
|
||||
@@ -95,9 +97,12 @@ init()
|
||||
:timer-remaining="store.timerRemaining"
|
||||
@choose="onChoose"
|
||||
/>
|
||||
<button v-if="started && !store.gameEnded" class="menu-trigger" @click="toggleMenu">
|
||||
菜单
|
||||
</button>
|
||||
<div v-if="started && !store.gameEnded" class="top-bar">
|
||||
<button class="top-btn" @click="toggleFullscreen" :title="isFullscreen ? '退出全屏' : '全屏'">
|
||||
{{ isFullscreen ? '⛶' : '⛶' }}
|
||||
</button>
|
||||
<button class="top-btn" @click="toggleMenu">菜单</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!started" class="start-overlay">
|
||||
<button class="start-btn" @click="handleStart">开始游戏</button>
|
||||
@@ -160,11 +165,16 @@ html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu-trigger {
|
||||
.top-bar {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.top-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
@@ -175,7 +185,7 @@ html, body {
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.menu-trigger:hover {
|
||||
.top-btn:hover {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
28
src/composables/useFullscreen.ts
Normal file
28
src/composables/useFullscreen.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useFullscreen() {
|
||||
const isFullscreen = ref(false)
|
||||
|
||||
function sync() {
|
||||
isFullscreen.value = !!document.fullscreenElement
|
||||
}
|
||||
|
||||
async function toggle() {
|
||||
if (!document.fullscreenElement) {
|
||||
await document.documentElement.requestFullscreen().catch(() => {})
|
||||
} else {
|
||||
await document.exitFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('fullscreenchange', sync)
|
||||
sync()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('fullscreenchange', sync)
|
||||
})
|
||||
|
||||
return { isFullscreen, toggle }
|
||||
}
|
||||
Reference in New Issue
Block a user