Spaces:
Running
Running
Upload 9 files
Browse files- src/views/StudentView.js +102 -22
src/views/StudentView.js
CHANGED
|
@@ -179,7 +179,7 @@ export async function renderStudentView() {
|
|
| 179 |
const monster = getNextMonster(actualStage, totalLikes, classSize);
|
| 180 |
|
| 181 |
return `
|
| 182 |
-
<div class="fixed top-
|
| 183 |
<!-- Monster -->
|
| 184 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center hover:scale-110" style="transform: scale(${currentScale});">
|
| 185 |
<div class="pixel-monster w-28 h-28 drop-shadow-2xl filter" style="animation: breathe 3s infinite ease-in-out;">
|
|
@@ -192,20 +192,15 @@ export async function renderStudentView() {
|
|
| 192 |
</div>
|
| 193 |
</div>
|
| 194 |
|
|
|
|
| 195 |
<!-- Evolution Prompt -->
|
| 196 |
${canEvolve ? `
|
| 197 |
-
<div class="absolute top-full mt-4
|
| 198 |
-
<
|
| 199 |
-
|
| 200 |
-
<
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
咦,小怪獸的樣子正在發生變化...<br>是否要進化?
|
| 204 |
-
</p>
|
| 205 |
-
<button onclick="window.triggerEvolution(${actualStage + 1})" class="w-full bg-pink-600 hover:bg-pink-500 text-white text-xs font-bold py-1.5 rounded-lg transition-colors shadow-sm">
|
| 206 |
-
✨ 立即進化
|
| 207 |
-
</button>
|
| 208 |
-
</div>
|
| 209 |
</div>
|
| 210 |
` : ''}
|
| 211 |
|
|
@@ -567,19 +562,104 @@ window.handleLike = async (progressId, targetUserId) => {
|
|
| 567 |
}
|
| 568 |
};
|
| 569 |
|
| 570 |
-
window.triggerEvolution = async (nextStage) => {
|
| 571 |
-
|
| 572 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
|
| 574 |
try {
|
|
|
|
| 575 |
const { updateUserStage } = await import("../services/classroom.js");
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 581 |
} catch (e) {
|
| 582 |
console.error(e);
|
| 583 |
-
alert("進化失敗");
|
|
|
|
| 584 |
}
|
| 585 |
};
|
|
|
|
| 179 |
const monster = getNextMonster(actualStage, totalLikes, classSize);
|
| 180 |
|
| 181 |
return `
|
| 182 |
+
<div id="monster-container-fixed" class="fixed top-24 left-12 sm:left-32 z-50 flex flex-col items-center group pointer-events-none sm:pointer-events-auto">
|
| 183 |
<!-- Monster -->
|
| 184 |
<div class="pixel-art-container relative transform transition-transform duration-500 ease-out origin-center hover:scale-110" style="transform: scale(${currentScale});">
|
| 185 |
<div class="pixel-monster w-28 h-28 drop-shadow-2xl filter" style="animation: breathe 3s infinite ease-in-out;">
|
|
|
|
| 192 |
</div>
|
| 193 |
</div>
|
| 194 |
|
| 195 |
+
<!-- Evolution Prompt -->
|
| 196 |
<!-- Evolution Prompt -->
|
| 197 |
${canEvolve ? `
|
| 198 |
+
<div id="evolution-prompt" class="absolute top-full mt-4 w-40 pointer-events-auto animate-bounce">
|
| 199 |
+
<button onclick="window.triggerEvolution(${actualStage}, ${actualStage + 1}, ${totalLikes}, ${classSize})"
|
| 200 |
+
class="w-full bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-500 hover:to-purple-500 text-white p-3 rounded-2xl shadow-[0_0_15px_rgba(236,72,153,0.6)] border border-white/20 transition-all hover:scale-105 active:scale-95 flex flex-col items-center gap-1 group-btn">
|
| 201 |
+
<span class="text-xs text-pink-100 font-bold">小怪獸要進化了!</span>
|
| 202 |
+
<span class="text-sm font-black text-white group-btn-hover:text-yellow-300">⚡ 點擊進化</span>
|
| 203 |
+
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
</div>
|
| 205 |
` : ''}
|
| 206 |
|
|
|
|
| 562 |
}
|
| 563 |
};
|
| 564 |
|
| 565 |
+
window.triggerEvolution = async (currentStage, nextStage, likes, classSize) => {
|
| 566 |
+
// 1. Hide Prompt
|
| 567 |
+
const prompt = document.getElementById('evolution-prompt');
|
| 568 |
+
if (prompt) prompt.style.display = 'none';
|
| 569 |
+
|
| 570 |
+
// 2. Prepare Animation Data
|
| 571 |
+
// We need Next Monster Data
|
| 572 |
+
// We can't easily import logic here if not exposed, but we exported getNextMonster.
|
| 573 |
+
// We need to re-import or use the one in scope if available.
|
| 574 |
+
// Fortunately setupStudentEvents is a module, but this function is on window.
|
| 575 |
+
// We need to pass data or use a helper.
|
| 576 |
+
// Ideally we should move getNextMonster to a global helper or fetch it.
|
| 577 |
+
// Let's use dynamic import to be safe and robust.
|
| 578 |
|
| 579 |
try {
|
| 580 |
+
const { getNextMonster, generateMonsterSVG, MONSTER_STAGES } = await import("../utils/monsterUtils.js");
|
| 581 |
const { updateUserStage } = await import("../services/classroom.js");
|
| 582 |
+
|
| 583 |
+
const currentMonster = getNextMonster(currentStage, likes, classSize);
|
| 584 |
+
const nextMonster = getNextMonster(nextStage, likes, classSize);
|
| 585 |
+
|
| 586 |
+
const container = document.querySelector('#monster-container-fixed .pixel-monster');
|
| 587 |
+
const containerWrapper = document.querySelector('#monster-container-fixed .pixel-art-container');
|
| 588 |
+
|
| 589 |
+
// Stop breathing animation
|
| 590 |
+
container.style.animation = 'none';
|
| 591 |
+
|
| 592 |
+
// --- ANIMATION SEQUENCE ---
|
| 593 |
+
// flicker count
|
| 594 |
+
let count = 0;
|
| 595 |
+
const maxFlickers = 10;
|
| 596 |
+
let speed = 300; // start slow
|
| 597 |
+
|
| 598 |
+
const svgCurrent = generateMonsterSVG(currentMonster);
|
| 599 |
+
const svgNext = generateMonsterSVG(nextMonster);
|
| 600 |
+
|
| 601 |
+
// Helper to set Content and Style
|
| 602 |
+
const setFrame = (svg, isSilhouette) => {
|
| 603 |
+
container.innerHTML = svg;
|
| 604 |
+
container.style.filter = isSilhouette ? 'brightness(0) invert(1)' : 'none'; // White silhouette? User said 'silhouette' usually black or white. Let's try Black (brightness 0)
|
| 605 |
+
if (isSilhouette) container.style.filter = 'brightness(0)';
|
| 606 |
+
};
|
| 607 |
+
|
| 608 |
+
const playFlicker = () => {
|
| 609 |
+
// Alternate
|
| 610 |
+
const isNext = count % 2 === 1;
|
| 611 |
+
setFrame(isNext ? svgNext : svgCurrent, true);
|
| 612 |
+
|
| 613 |
+
count++;
|
| 614 |
+
|
| 615 |
+
if (count < maxFlickers) {
|
| 616 |
+
// Speed up
|
| 617 |
+
speed *= 0.8;
|
| 618 |
+
setTimeout(playFlicker, speed);
|
| 619 |
+
} else {
|
| 620 |
+
// Final Reveal
|
| 621 |
+
setTimeout(() => {
|
| 622 |
+
// Pause on Next Silhouette
|
| 623 |
+
setFrame(svgNext, true);
|
| 624 |
+
|
| 625 |
+
setTimeout(() => {
|
| 626 |
+
// Reveal Color with flash
|
| 627 |
+
containerWrapper.style.transition = 'filter 0.5s ease-out';
|
| 628 |
+
containerWrapper.style.filter = 'drop-shadow(0 0 20px #ffffff)'; // Flash
|
| 629 |
+
|
| 630 |
+
setFrame(svgNext, false); // Color
|
| 631 |
+
|
| 632 |
+
setTimeout(async () => {
|
| 633 |
+
containerWrapper.style.filter = 'none';
|
| 634 |
+
// DB Update
|
| 635 |
+
const userId = localStorage.getItem('vibecoding_user_id');
|
| 636 |
+
await updateUserStage(userId, nextStage);
|
| 637 |
+
// Reload
|
| 638 |
+
const app = document.querySelector('#app');
|
| 639 |
+
// We need to re-import renderStudentView? It's exported.
|
| 640 |
+
// But we are inside window function.
|
| 641 |
+
// Just generic reload for now or try to re-render if accessible.
|
| 642 |
+
// renderStudentView is not global.
|
| 643 |
+
// Let's reload page to be cleanest or rely on the subscribeToUserProgress which might flicker?
|
| 644 |
+
// subscribeToUserProgress listens to PROGRESS collection, not USERS collection (where monster scale is).
|
| 645 |
+
// So we MUST reload or manually fetch profile.
|
| 646 |
+
// Simple reload:
|
| 647 |
+
// window.location.reload();
|
| 648 |
+
// Or better: triggering the view re-render.
|
| 649 |
+
// Note: We don't have access to 'renderStudentView' function here easily unless we attached it to window.
|
| 650 |
+
// Let's reload to ensure clean state.
|
| 651 |
+
window.location.reload();
|
| 652 |
+
}, 1000);
|
| 653 |
+
}, 800);
|
| 654 |
+
}, 200);
|
| 655 |
+
}
|
| 656 |
+
};
|
| 657 |
+
|
| 658 |
+
playFlicker();
|
| 659 |
+
|
| 660 |
} catch (e) {
|
| 661 |
console.error(e);
|
| 662 |
+
alert("進化失敗...");
|
| 663 |
+
window.location.reload();
|
| 664 |
}
|
| 665 |
};
|