Files
game/assets/story-player.js
T
2026-08-27 20:15:18 +08:00

107 lines
3.6 KiB
JavaScript

/**
* 宇森剧情漫画播放器
* 用法:
* await playIntroStory(['assets/story/story_01.jpg', 'assets/story/story_02.jpg']);
* 或:
* await playIntroStory(['assets/story/story_03.jpg'], { enterText: '进入世界 ▸', holdMs: 5000 });
*/
// 确保遮罩 DOM 存在(页面只需引入本脚本与 CSS)
(function ensureOverlay() {
if (document.getElementById('introOverlay')) return;
const overlay = document.createElement('div');
overlay.id = 'introOverlay';
overlay.innerHTML =
'<div class="introSkip" id="introSkip">跳过剧情 ▸</div>' +
'<img class="introImg" id="introImg" alt="剧情" />' +
'<button class="introEnter" id="introEnter">踏入宇森世界 ▸</button>' +
'<div class="introDots" id="introDots"></div>' +
'<div class="introHint" id="introHint">点击 / 空格 / → 继续</div>';
document.body.appendChild(overlay);
})();
window.playIntroStory = function playIntroStory(panels, options) {
options = options || {};
const holdMs = options.holdMs || 5000;
const enterText = options.enterText || '踏入宇森世界 ▸';
const onStart = options.onStart || null; // 开始播放前回调,用于隐藏页面原生元素
return new Promise((resolve) => {
const overlay = document.getElementById('introOverlay');
const img = document.getElementById('introImg');
const dotsWrap = document.getElementById('introDots');
const hint = document.getElementById('introHint');
const skipBtn = document.getElementById('introSkip');
const enterBtn = document.getElementById('introEnter');
if (!Array.isArray(panels) || panels.length === 0) {
resolve();
return;
}
if (typeof onStart === 'function') onStart();
enterBtn.textContent = enterText;
let idx = 0, autoTimer = null, done = false, dots = [];
dotsWrap.innerHTML = '';
panels.forEach((_, i) => {
const d = document.createElement('div');
d.className = 'introDot' + (i === 0 ? ' on' : '');
dotsWrap.appendChild(d);
});
dots = dotsWrap.querySelectorAll('.introDot');
function showPanel(i) {
idx = i;
dots.forEach((d, k) => d.classList.toggle('on', k === i));
img.classList.remove('show');
img.onload = () => img.classList.add('show');
img.src = panels[i];
if (img.complete) img.classList.add('show');
const last = (i === panels.length - 1);
enterBtn.classList.toggle('show', last);
hint.style.opacity = last ? '0' : '1';
clearTimeout(autoTimer);
if (!last) autoTimer = setTimeout(nextPanel, holdMs);
}
function nextPanel() {
if (done || idx >= panels.length - 1) return;
showPanel(idx + 1);
}
function finish() {
if (done) return;
done = true;
clearTimeout(autoTimer);
document.removeEventListener('keydown', onKey);
overlay.classList.remove('active');
setTimeout(() => { overlay.style.display = 'none'; }, 500);
resolve();
}
function onKey(e) {
if (!overlay.classList.contains('active')) return;
if (e.key === ' ' || e.key === 'Enter' || e.key === 'ArrowRight') {
e.preventDefault();
if (idx === panels.length - 1) finish(); else nextPanel();
} else if (e.key === 'Escape') {
finish();
}
}
overlay.addEventListener('click', (e) => {
if (e.target === skipBtn || e.target === enterBtn) return;
nextPanel();
});
skipBtn.addEventListener('click', finish);
enterBtn.addEventListener('click', finish);
document.addEventListener('keydown', onKey);
overlay.style.display = 'flex';
requestAnimationFrame(() => overlay.classList.add('active'));
showPanel(0);
});
};