Spaces:
Running
Running
Upload 3 files
Browse files- .gitattributes +2 -0
- Sacred Dawn Ascending.mp3 +3 -0
- Singularity Drift.mp3 +3 -0
- index.html +106 -6
.gitattributes
CHANGED
|
@@ -36,3 +36,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 36 |
m1.GIF filter=lfs diff=lfs merge=lfs -text
|
| 37 |
m4.GIF filter=lfs diff=lfs merge=lfs -text
|
| 38 |
pigod.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 36 |
m1.GIF filter=lfs diff=lfs merge=lfs -text
|
| 37 |
m4.GIF filter=lfs diff=lfs merge=lfs -text
|
| 38 |
pigod.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
Sacred[[:space:]]Dawn[[:space:]]Ascending.mp3 filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
Singularity[[:space:]]Drift.mp3 filter=lfs diff=lfs merge=lfs -text
|
Sacred Dawn Ascending.mp3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:550d42b62277e1e12b680279fefec3a34802345dafd532776a68c7c230505344
|
| 3 |
+
size 4947564
|
Singularity Drift.mp3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e5a59f965320ff9286c27200d899e4e6a6a96c72cf6a036f861d58b239012721
|
| 3 |
+
size 9653300
|
index.html
CHANGED
|
@@ -274,7 +274,9 @@
|
|
| 274 |
<!-- Excel 匯入面板 -->
|
| 275 |
<div class="glass-panel" style="margin-bottom: 2rem; background: rgba(0,0,0,0.4); border: 1px dashed var(--primary);">
|
| 276 |
<h2 style="color: var(--accent); margin-top: 0; font-size: 1.8rem; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">📁 賜下預先名冊 (Excel 匯入)</h2>
|
| 277 |
-
<p style="color: #d1d5db; margin-bottom: 1rem;">可先從 Excel 匯入檔案自動尋找第一名。檔案需至少兩欄:第一欄姓名、第二欄數字(不含標頭第一行)。
|
|
|
|
|
|
|
| 278 |
<input type="file" id="excelFile" accept=".xlsx, .xls" style="color: white; margin-bottom: 1rem; width: 100%; padding: 0.5rem; border: 1px solid rgba(255,255,255,0.2); border-radius: 0.5rem; background: rgba(0,0,0,0.3);"/>
|
| 279 |
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;">
|
| 280 |
<button class="btn" id="importBtn" style="width: auto; padding: 0.75rem 1.5rem; font-size: 1.2rem;">匯入並預先請求神諭</button>
|
|
@@ -288,14 +290,19 @@
|
|
| 288 |
</div>
|
| 289 |
|
| 290 |
<div class="glass-panel" style="margin-bottom: 2rem;">
|
| 291 |
-
<div style="display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 1.5rem; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 1rem;">
|
| 292 |
<div>
|
| 293 |
<h2 style="margin: 0; font-size: 1.8rem; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">👨🎓 信徒等待區</h2>
|
| 294 |
<p style="margin: 0.5rem 0 0 0; color: #d1d5db;">目前已收到 <strong id="studentCount" style="color: white; font-size: 1.2rem;">0</strong> 份幸運數字</p>
|
| 295 |
</div>
|
| 296 |
-
<
|
| 297 |
-
|
| 298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
</div>
|
| 300 |
<!-- 卡片列表動態產生於此 -->
|
| 301 |
<div class="students-grid" id="studentsGrid"></div>
|
|
@@ -329,6 +336,73 @@
|
|
| 329 |
<script type="module">
|
| 330 |
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.1/firebase-app.js";
|
| 331 |
import { getFirestore, doc, collection, onSnapshot, query, orderBy, getDocs, limit, setDoc, deleteDoc, updateDoc, where } from "https://www.gstatic.com/firebasejs/10.8.1/firebase-firestore.js";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
|
| 333 |
const firebaseConfig = {
|
| 334 |
apiKey: "AIzaSyA2fxxaGAdWSR_QOH2Hm92kttGZmLDH8-w",
|
|
@@ -408,6 +482,7 @@
|
|
| 408 |
// --- 0. 開場動畫點擊 ---
|
| 409 |
const introScreen = document.getElementById('introScreen');
|
| 410 |
introScreen.addEventListener('click', () => {
|
|
|
|
| 411 |
introScreen.style.opacity = '0';
|
| 412 |
setTimeout(() => {
|
| 413 |
introScreen.style.display = 'none';
|
|
@@ -512,8 +587,13 @@
|
|
| 512 |
text: studentUrl, width: 300, height: 300, colorDark : "#000000", colorLight : "#ffffff", correctLevel : QRCode.CorrectLevel.L
|
| 513 |
});
|
| 514 |
|
| 515 |
-
document.getElementById('qrcodeArea').onclick = () =>
|
|
|
|
|
|
|
|
|
|
| 516 |
document.getElementById('qrModal').onclick = () => document.getElementById('qrModal').style.display = 'none';
|
|
|
|
|
|
|
| 517 |
|
| 518 |
// 啟動資料庫監聽
|
| 519 |
if(isTestMode) {
|
|
@@ -900,6 +980,25 @@
|
|
| 900 |
// 按數字大小從大排到小 (越深的人排名越高)
|
| 901 |
finalPool.sort((a, b) => b.resultVal - a.resultVal);
|
| 902 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
drawPodium(finalPool);
|
| 904 |
|
| 905 |
if(finalPool.length > 0) {
|
|
@@ -939,6 +1038,7 @@
|
|
| 939 |
}
|
| 940 |
|
| 941 |
function drawPodium(validData) {
|
|
|
|
| 942 |
const podiumArea = document.getElementById('podiumArea');
|
| 943 |
podiumArea.innerHTML = "";
|
| 944 |
|
|
|
|
| 274 |
<!-- Excel 匯入面板 -->
|
| 275 |
<div class="glass-panel" style="margin-bottom: 2rem; background: rgba(0,0,0,0.4); border: 1px dashed var(--primary);">
|
| 276 |
<h2 style="color: var(--accent); margin-top: 0; font-size: 1.8rem; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">📁 賜下預先名冊 (Excel 匯入)</h2>
|
| 277 |
+
<p style="color: #d1d5db; margin-bottom: 1rem;">可先從 Excel 匯入檔案自動尋找第一名。檔案需至少兩欄:第一欄姓名、第二欄數字(不含標頭第一行)。
|
| 278 |
+
<a href="https://docs.google.com/spreadsheets/d/11hXMTLU7rPeu9h6B-BNGF6esFWEXJSGo/edit?usp=drive_link&ouid=115947803934724851078&rtpof=true&sd=true" target="_blank" style="color: var(--accent); text-decoration: underline; margin-left: 0.5rem; white-space: nowrap;">📥 下載範例檔案</a>
|
| 279 |
+
</p>
|
| 280 |
<input type="file" id="excelFile" accept=".xlsx, .xls" style="color: white; margin-bottom: 1rem; width: 100%; padding: 0.5rem; border: 1px solid rgba(255,255,255,0.2); border-radius: 0.5rem; background: rgba(0,0,0,0.3);"/>
|
| 281 |
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;">
|
| 282 |
<button class="btn" id="importBtn" style="width: auto; padding: 0.75rem 1.5rem; font-size: 1.2rem;">匯入並預先請求神諭</button>
|
|
|
|
| 290 |
</div>
|
| 291 |
|
| 292 |
<div class="glass-panel" style="margin-bottom: 2rem;">
|
| 293 |
+
<div style="display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 1.5rem; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 1rem; flex-wrap: wrap; gap: 1rem;">
|
| 294 |
<div>
|
| 295 |
<h2 style="margin: 0; font-size: 1.8rem; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">👨🎓 信徒等待區</h2>
|
| 296 |
<p style="margin: 0.5rem 0 0 0; color: #d1d5db;">目前已收到 <strong id="studentCount" style="color: white; font-size: 1.2rem;">0</strong> 份幸運數字</p>
|
| 297 |
</div>
|
| 298 |
+
<div style="display: flex; gap: 1rem;">
|
| 299 |
+
<button class="btn" id="restartMusicBtn" style="width: auto; padding: 1rem 1.5rem; font-size: 1.2rem; background: rgba(59, 130, 246, 0.8); border-color: rgba(96, 165, 250, 0.5); color: white;" title="返回等候階段,重新播放背景音樂">
|
| 300 |
+
🎵 重啟等候音樂
|
| 301 |
+
</button>
|
| 302 |
+
<button class="btn" id="searchApiBtn" style="width: auto; padding: 1rem 3rem; font-size: 1.5rem;">
|
| 303 |
+
✨ 祈求眷顧!開始搜尋
|
| 304 |
+
</button>
|
| 305 |
+
</div>
|
| 306 |
</div>
|
| 307 |
<!-- 卡片列表動態產生於此 -->
|
| 308 |
<div class="students-grid" id="studentsGrid"></div>
|
|
|
|
| 336 |
<script type="module">
|
| 337 |
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.1/firebase-app.js";
|
| 338 |
import { getFirestore, doc, collection, onSnapshot, query, orderBy, getDocs, limit, setDoc, deleteDoc, updateDoc, where } from "https://www.gstatic.com/firebasejs/10.8.1/firebase-firestore.js";
|
| 339 |
+
|
| 340 |
+
// --- 音樂資源設定 ---
|
| 341 |
+
const audioDrift = new Audio('Singularity Drift.mp3');
|
| 342 |
+
audioDrift.loop = true;
|
| 343 |
+
audioDrift.volume = 0;
|
| 344 |
+
|
| 345 |
+
const audioDawn = new Audio('Sacred Dawn Ascending.mp3');
|
| 346 |
+
audioDawn.loop = true;
|
| 347 |
+
audioDawn.volume = 0;
|
| 348 |
+
|
| 349 |
+
let fadeInterval = null;
|
| 350 |
+
|
| 351 |
+
function playDrift() {
|
| 352 |
+
clearInterval(fadeInterval);
|
| 353 |
+
audioDrift.play().catch(e => console.warn("Audio play error:", e));
|
| 354 |
+
|
| 355 |
+
fadeInterval = setInterval(() => {
|
| 356 |
+
let driftVol = audioDrift.volume;
|
| 357 |
+
let dawnVol = audioDawn.volume;
|
| 358 |
+
let done = true;
|
| 359 |
+
|
| 360 |
+
if (dawnVol > 0.05) {
|
| 361 |
+
audioDawn.volume = Math.max(0, dawnVol - 0.05);
|
| 362 |
+
done = false;
|
| 363 |
+
} else {
|
| 364 |
+
audioDawn.volume = 0;
|
| 365 |
+
if(!audioDawn.paused) audioDawn.pause();
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
if (driftVol < 0.95) {
|
| 369 |
+
audioDrift.volume = Math.min(1, driftVol + 0.05);
|
| 370 |
+
done = false;
|
| 371 |
+
} else {
|
| 372 |
+
audioDrift.volume = 1;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
if (done) clearInterval(fadeInterval);
|
| 376 |
+
}, 100);
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
function playDawn() {
|
| 380 |
+
clearInterval(fadeInterval);
|
| 381 |
+
audioDawn.play().catch(e => console.warn("Audio play error:", e));
|
| 382 |
+
|
| 383 |
+
fadeInterval = setInterval(() => {
|
| 384 |
+
let driftVol = audioDrift.volume;
|
| 385 |
+
let dawnVol = audioDawn.volume;
|
| 386 |
+
let done = true;
|
| 387 |
+
|
| 388 |
+
if (driftVol > 0.05) {
|
| 389 |
+
audioDrift.volume = Math.max(0, driftVol - 0.05);
|
| 390 |
+
done = false;
|
| 391 |
+
} else {
|
| 392 |
+
audioDrift.volume = 0;
|
| 393 |
+
if(!audioDrift.paused) audioDrift.pause();
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
if (dawnVol < 0.95) {
|
| 397 |
+
audioDawn.volume = Math.min(1, dawnVol + 0.05);
|
| 398 |
+
done = false;
|
| 399 |
+
} else {
|
| 400 |
+
audioDawn.volume = 1;
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
if (done) clearInterval(fadeInterval);
|
| 404 |
+
}, 100);
|
| 405 |
+
}
|
| 406 |
|
| 407 |
const firebaseConfig = {
|
| 408 |
apiKey: "AIzaSyA2fxxaGAdWSR_QOH2Hm92kttGZmLDH8-w",
|
|
|
|
| 482 |
// --- 0. 開場動畫點擊 ---
|
| 483 |
const introScreen = document.getElementById('introScreen');
|
| 484 |
introScreen.addEventListener('click', () => {
|
| 485 |
+
playDrift(); // 🎵 開始播放背景音樂
|
| 486 |
introScreen.style.opacity = '0';
|
| 487 |
setTimeout(() => {
|
| 488 |
introScreen.style.display = 'none';
|
|
|
|
| 587 |
text: studentUrl, width: 300, height: 300, colorDark : "#000000", colorLight : "#ffffff", correctLevel : QRCode.CorrectLevel.L
|
| 588 |
});
|
| 589 |
|
| 590 |
+
document.getElementById('qrcodeArea').onclick = () => {
|
| 591 |
+
document.getElementById('qrModal').style.display = 'flex';
|
| 592 |
+
playDrift(); // 自動重新播放等候音樂 (第二階段掃碼時)
|
| 593 |
+
};
|
| 594 |
document.getElementById('qrModal').onclick = () => document.getElementById('qrModal').style.display = 'none';
|
| 595 |
+
// 手動按鈕重新播放音樂
|
| 596 |
+
document.getElementById('restartMusicBtn').onclick = () => playDrift();
|
| 597 |
|
| 598 |
// 啟動資料庫監聽
|
| 599 |
if(isTestMode) {
|
|
|
|
| 980 |
// 按數字大小從大排到小 (越深的人排名越高)
|
| 981 |
finalPool.sort((a, b) => b.resultVal - a.resultVal);
|
| 982 |
|
| 983 |
+
// 排列名次並將最終總排名推播至 Firebase 讓學生端更新
|
| 984 |
+
let currentRank = 1;
|
| 985 |
+
for (let i = 0; i < finalPool.length; i++) {
|
| 986 |
+
// 若分數與前一名不同,名次改為目前人數順序 (並列名次處理)
|
| 987 |
+
if (i > 0 && finalPool[i].resultVal < finalPool[i-1].resultVal) {
|
| 988 |
+
currentRank = i + 1;
|
| 989 |
+
}
|
| 990 |
+
finalPool[i].rank = currentRank;
|
| 991 |
+
|
| 992 |
+
// 若非 Excel 資料 (即現場真實手機連線資料),回寫 Firebase
|
| 993 |
+
if (!finalPool[i].id.startsWith('excel_') && !isTestMode) {
|
| 994 |
+
try {
|
| 995 |
+
updateDoc(doc(db, "rooms", currentRoom, "students", finalPool[i].id), {
|
| 996 |
+
rank: currentRank
|
| 997 |
+
});
|
| 998 |
+
} catch(e) {}
|
| 999 |
+
}
|
| 1000 |
+
}
|
| 1001 |
+
|
| 1002 |
drawPodium(finalPool);
|
| 1003 |
|
| 1004 |
if(finalPool.length > 0) {
|
|
|
|
| 1038 |
}
|
| 1039 |
|
| 1040 |
function drawPodium(validData) {
|
| 1041 |
+
playDawn(); // 🎵 結果出爐,切換為 Sacred Dawn Ascending.mp3 音樂
|
| 1042 |
const podiumArea = document.getElementById('podiumArea');
|
| 1043 |
podiumArea.innerHTML = "";
|
| 1044 |
|