Enlace a la aplicación html
Aplicación html en un Site
Código html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Juega y Aprende los Colores</title>
<style>
/* =========================================================
VARIABLES Y CONFIGURACIÓN GLOBAL
========================================================= */
:root {
--bg-color: #E0F7FA;
--text-color: #333;
--shadow: 0 6px 0 rgba(0,0,0,0.2);
--shadow-active: 0 0px 0 rgba(0,0,0,0.2);
--border-radius: 20px;
}
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
user-select: none;
touch-action: manipulation;
}
body {
margin: 0;
padding: 0;
font-family: 'Comic Sans MS', 'Chalkboard SE', 'Marker Felt', sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
overflow: hidden;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
/* =========================================================
NAVEGACIÓN Y PANTALLAS
========================================================= */
.screen {
display: none;
width: 100%;
height: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 20px;
position: absolute;
top: 0;
left: 0;
background-color: var(--bg-color);
}
.screen.active {
display: flex;
animation: fadeIn 0.4s ease-out;
}
header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
z-index: 10;
}
.btn-icon {
background: #fff;
border: 3px solid #ccc;
border-radius: 50%;
width: 60px;
height: 60px;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: var(--shadow);
transition: all 0.1s;
}
.btn-icon:active {
transform: translateY(6px);
box-shadow: var(--shadow-active);
}
h1 {
font-size: 5vh;
text-align: center;
margin: 10px 0;
color: #FF2D55;
text-shadow: 2px 2px 0px #fff;
}
h2.title-instruccion {
font-size: 4vh;
text-align: center;
margin: 5px 0 20px 0;
background: rgba(255,255,255,0.7);
padding: 10px 20px;
border-radius: var(--border-radius);
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
/* =========================================================
BOTONES PRINCIPALES
========================================================= */
.menu-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
width: 100%;
max-width: 800px;
flex-grow: 1;
align-content: center;
}
.btn-large {
font-family: inherit;
font-size: 24px;
font-weight: bold;
padding: 20px;
border-radius: var(--border-radius);
border: 4px solid rgba(0,0,0,0.1);
color: white;
cursor: pointer;
box-shadow: var(--shadow);
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
transition: transform 0.1s;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
}
.btn-large:active {
transform: translateY(6px);
box-shadow: var(--shadow-active);
}
/* Colores de los botones del menú */
.bg-red { background-color: #FF3B30; }
.bg-blue { background-color: #007AFF; }
.bg-yellow { background-color: #FFCC00; color: #333; text-shadow: none;}
.bg-green { background-color: #34C759; }
.bg-purple { background-color: #AF52DE; }
.bg-orange { background-color: #FF9500; }
/* =========================================================
JUEGO 1: ENCUENTRA EL COLOR
========================================================= */
#g1-circles {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
width: 100%;
max-width: 600px;
}
.color-circle {
width: 120px;
height: 120px;
border-radius: 50%;
border: 5px solid white;
box-shadow: 0 8px 15px rgba(0,0,0,0.2);
cursor: pointer;
transition: transform 0.2s;
}
.color-circle:active {
transform: scale(0.9);
}
/* =========================================================
JUEGO 2: ¿DE QUÉ COLOR ES?
========================================================= */
.g2-drawing-container {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-height: 40vh;
}
.g2-drawing-container svg {
width: 100%;
height: 100%;
max-width: 300px;
filter: drop-shadow(0 10px 10px rgba(0,0,0,0.2));
animation: bounceIn 0.5s ease-out;
}
#g2-options {
display: flex;
justify-content: center;
gap: 15px;
width: 100%;
padding: 20px;
flex-wrap: wrap;
}
/* =========================================================
JUEGO 3: MEMORIA
========================================================= */
#g3-board {
display: grid;
gap: 15px;
width: 100%;
max-width: 600px;
justify-content: center;
align-content: center;
flex-grow: 1;
}
.memory-card {
width: 80px;
height: 80px;
background-color: white;
border-radius: 15px;
box-shadow: 0 5px 10px rgba(0,0,0,0.15);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s;
transform-style: preserve-3d;
position: relative;
border: 4px solid #ddd;
}
.memory-card.flipped {
transform: rotateY(180deg);
}
.memory-card .front, .memory-card .back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 11px;
}
.memory-card .back {
background-image: radial-gradient(#FF9500 20%, transparent 20%), radial-gradient(#FF9500 20%, transparent 20%);
background-color: #FFCC00;
background-position: 0 0, 10px 10px;
background-size: 20px 20px;
}
.memory-card .front {
transform: rotateY(180deg);
background-color: white; /* Color dinámico aplicado por JS */
}
#g3-stats {
display: flex;
justify-content: space-around;
width: 100%;
max-width: 400px;
font-size: 24px;
background: white;
padding: 10px;
border-radius: 20px;
margin-bottom: 10px;
}
/* =========================================================
JUEGO 4: EXPLOTA EL COLOR
========================================================= */
#g4-container {
position: relative;
width: 100%;
flex-grow: 1;
overflow: hidden;
border-radius: var(--border-radius);
background: linear-gradient(to bottom, #87CEEB, #E0F7FA);
box-shadow: inset 0 0 20px rgba(0,0,0,0.1);
}
.bubble {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.8), transparent 40%), var(--b-color);
box-shadow: inset -5px -5px 15px rgba(0,0,0,0.2), 0 5px 10px rgba(0,0,0,0.1);
cursor: pointer;
bottom: -150px;
animation: floatUp linear infinite;
}
@keyframes floatUp {
0% { transform: translateY(0) scale(1); }
50% { transform: translateY(-60vh) scale(1.1); }
100% { transform: translateY(-120vh) scale(1); }
}
.popped {
animation: pop 0.2s ease-out forwards;
}
/* =========================================================
JUEGO 5: COLOREA
========================================================= */
#g5-canvas-container {
flex-grow: 1;
width: 100%;
max-width: 600px;
background: white;
border-radius: var(--border-radius);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
padding: 10px;
}
#g5-canvas-container svg {
width: 100%;
height: 100%;
max-height: 40vh;
}
.fillable {
cursor: pointer;
stroke: #000;
stroke-width: 4;
stroke-linejoin: round;
transition: fill 0.2s;
}
.fillable:hover {
opacity: 0.8;
}
#g5-palette {
display: flex;
justify-content: center;
gap: 10px;
padding: 15px;
width: 100%;
overflow-x: auto;
background: rgba(255,255,255,0.8);
border-radius: var(--border-radius);
margin-top: 10px;
}
.palette-color {
width: 50px;
height: 50px;
min-width: 50px;
border-radius: 50%;
border: 4px solid white;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
cursor: pointer;
}
.palette-color.selected {
border-color: #333;
transform: scale(1.2);
}
.g5-controls {
display: flex;
gap: 10px;
margin-top: 10px;
}
.btn-small {
padding: 10px 20px;
font-size: 20px;
border-radius: 15px;
border: 3px solid rgba(0,0,0,0.1);
background: #fff;
cursor: pointer;
box-shadow: 0 4px 0 rgba(0,0,0,0.1);
font-family: inherit;
font-weight: bold;
}
.btn-small:active {
transform: translateY(4px);
box-shadow: none;
}
/* =========================================================
ANIMACIONES GLOBALES Y MOTIVACIÓN
========================================================= */
#motivation-overlay {
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: rgba(255,255,255,0.9);
z-index: 100;
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
}
#motivation-overlay.show {
display: flex;
animation: fadeIn 0.3s ease-out;
}
#motivation-text {
font-size: 10vw;
color: #FF9500;
text-shadow: 4px 4px 0 #FFF, 8px 8px 0px rgba(0,0,0,0.1);
animation: zoomBounce 1s cubic-bezier(0.175, 0.885, 0.32, 1.275);
text-align: center;
}
.confetti {
position: absolute;
width: 20px; height: 20px;
background: red;
animation: fall 3s linear infinite;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px) rotate(-5deg); }
75% { transform: translateX(10px) rotate(5deg); }
}
.wrong-shake { animation: shake 0.4s ease-in-out; }
@keyframes bounceIn {
0% { transform: scale(0); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
@keyframes zoomBounce {
0% { transform: scale(0); opacity: 0; }
70% { transform: scale(1.2); opacity: 1; }
100% { transform: scale(1); }
}
@keyframes pop {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.8; }
100% { transform: scale(0); opacity: 0; }
}
@keyframes fall {
0% { transform: translateY(-100vh) rotate(0deg); }
100% { transform: translateY(100vh) rotate(360deg); }
}
/* Media Queries para móviles pequeños */
@media (max-width: 400px) {
.color-circle { width: 90px; height: 90px; }
.memory-card { width: 65px; height: 65px; }
h2.title-instruccion { font-size: 3.5vh; }
}
</style>
</head>
<body>
<!-- OVERLAY MOTIVACIONAL -->
<div id="motivation-overlay">
<div id="motivation-text">¡Muy bien!</div>
</div>
<!-- PANTALLA: MENÚ PRINCIPAL -->
<div id="screen-menu" class="screen active">
<h1>🎨 Juega con Colores</h1>
<div class="menu-grid">
<button class="btn-large bg-red" onclick="App.showDifficulty('g1')">
<span style="font-size: 40px;">🎯</span>
Encuentra el color
</button>
<button class="btn-large bg-blue" onclick="App.showDifficulty('g2')">
<span style="font-size: 40px;">❓</span>
¿De qué color es?
</button>
<button class="btn-large bg-yellow" onclick="App.showDifficulty('g3')">
<span style="font-size: 40px;">🧩</span>
Memoria
</button>
<button class="btn-large bg-green" onclick="App.showDifficulty('g4')">
<span style="font-size: 40px;">💥</span>
Explota el color
</button>
<button class="btn-large bg-purple" onclick="App.showDifficulty('g5')">
<span style="font-size: 40px;">🖌️</span>
Colorea
</button>
</div>
<div style="margin-top: 20px;">
<button class="btn-icon" id="btn-sound-toggle" onclick="AudioEngine.toggleSound()">🔊</button>
</div>
</div>
<!-- PANTALLA: SELECCIÓN DE DIFICULTAD -->
<div id="screen-difficulty" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">⬅️</button>
<button class="btn-icon" onclick="AudioEngine.toggleSound()" class="sound-icon">🔊</button>
</header>
<h1>Nivel de dificultad</h1>
<div class="menu-grid" style="margin-top: 30px;">
<button class="btn-large bg-green" onclick="App.startGame('easy')">
Fácil (4 colores)
</button>
<button class="btn-large bg-orange" onclick="App.startGame('medium')">
Medio (8 colores)
</button>
<button class="btn-large bg-red" onclick="App.startGame('hard')">
Difícil (11 colores)
</button>
</div>
</div>
<!-- PANTALLA: JUEGO 1 (Encuentra el color) -->
<div id="screen-g1" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">🏠</button>
<div style="font-size: 30px; font-weight: bold; color: #FF9500;">⭐ <span id="g1-score">0</span></div>
</header>
<h2 class="title-instruccion" id="g1-instruction">Toca el color...</h2>
<div id="g1-circles"></div>
</div>
<!-- PANTALLA: JUEGO 2 (¿De qué color es?) -->
<div id="screen-g2" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">🏠</button>
<div style="font-size: 30px; font-weight: bold; color: #FF9500;">⭐ <span id="g2-score">0</span></div>
</header>
<h2 class="title-instruccion">¿De qué color es?</h2>
<div class="g2-drawing-container" id="g2-drawing"></div>
<div id="g2-options"></div>
</div>
<!-- PANTALLA: JUEGO 3 (Memoria) -->
<div id="screen-g3" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">🏠</button>
<button class="btn-icon" onclick="Games.G3.init()">🔄</button>
</header>
<div id="g3-stats">
<span>Pasos: <span id="g3-moves">0</span></span>
<span>Parejas: <span id="g3-pairs">0</span></span>
</div>
<div id="g3-board"></div>
</div>
<!-- PANTALLA: JUEGO 4 (Explota burbujas) -->
<div id="screen-g4" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">🏠</button>
<div style="font-size: 30px; font-weight: bold; color: #FF9500;">Faltan: <span id="g4-remaining">0</span></div>
</header>
<h2 class="title-instruccion" id="g4-instruction" style="position: absolute; top: 80px; z-index: 10;">Explota las burbujas...</h2>
<div id="g4-container"></div>
</div>
<!-- PANTALLA: JUEGO 5 (Colorea) -->
<div id="screen-g5" class="screen">
<header>
<button class="btn-icon" onclick="App.setScreen('menu')">🏠</button>
</header>
<h2 class="title-instruccion" style="margin-bottom: 5px;">¡A pintar!</h2>
<div id="g5-canvas-container"></div>
<div id="g5-palette"></div>
<div class="g5-controls">
<button class="btn-small" onclick="Games.G5.prevImage()">⬅️</button>
<button class="btn-small" onclick="Games.G5.clearCanvas()">Borrar 🗑️</button>
<button class="btn-small" onclick="Games.G5.nextImage()">➡️</button>
</div>
</div>
<script>
/* =========================================================
DATOS PRINCIPALES (Colores y Gráficos SVG)
========================================================= */
const COLORS_DATA = [
{ id: 'rojo', hex: '#FF3B30', name: 'rojo' },
{ id: 'azul', hex: '#007AFF', name: 'azul' },
{ id: 'amarillo', hex: '#FFCC00', name: 'amarillo' },
{ id: 'verde', hex: '#34C759', name: 'verde' },
{ id: 'naranja', hex: '#FF9500', name: 'naranja' },
{ id: 'morado', hex: '#AF52DE', name: 'morado' },
{ id: 'rosa', hex: '#FF2D55', name: 'rosa' },
{ id: 'marron', hex: '#8D6E63', name: 'marrón' },
{ id: 'negro', hex: '#212121', name: 'negro' },
{ id: 'blanco', hex: '#FFFFFF', name: 'blanco' },
{ id: 'gris', hex: '#9E9E9E', name: 'gris' }
];
// SVGs coloreados para Juego 2 (¿De qué color es?)
const SVGS_COLORED = [
{ id: 'manzana', colorId: 'rojo', svg: `<svg viewBox="0 0 100 100"><path fill="#FF3B30" d="M50,90 C20,90 10,60 10,40 C10,15 35,10 50,25 C65,10 90,15 90,40 C90,60 80,90 50,90 Z"/><path fill="#4CAF50" d="M50,25 C40,10 55,0 70,5 C60,20 50,25 50,25 Z"/></svg>` },
{ id: 'sol', colorId: 'amarillo', svg: `<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="25" fill="#FFCC00"/><path stroke="#FFCC00" stroke-width="6" stroke-linecap="round" d="M50,10 L50,20 M50,80 L50,90 M10,50 L20,50 M80,50 L90,50 M22,22 L29,29 M71,71 L78,78 M22,78 L29,71 M71,22 L78,29"/></svg>` },
{ id: 'rana', colorId: 'verde', svg: `<svg viewBox="0 0 100 100"><ellipse cx="50" cy="60" rx="40" ry="30" fill="#34C759"/><circle cx="30" cy="35" r="15" fill="#34C759"/><circle cx="70" cy="35" r="15" fill="#34C759"/><circle cx="30" cy="35" r="5" fill="#FFF"/><circle cx="70" cy="35" r="5" fill="#FFF"/><path d="M 30 65 Q 50 80 70 65" stroke="#212121" stroke-width="3" fill="transparent"/></svg>` },
{ id: 'nube', colorId: 'blanco', svg: `<svg viewBox="0 0 100 100"><path fill="#FFFFFF" stroke="#ddd" stroke-width="2" d="M 30,70 A 20,20 0 0,1 30,30 A 25,25 0 0,1 70,30 A 20,20 0 0,1 70,70 Z"/></svg>` },
{ id: 'mar', colorId: 'azul', svg: `<svg viewBox="0 0 100 100"><rect x="10" y="40" width="80" height="50" fill="#007AFF" rx="10"/><path d="M 10,40 Q 20,30 30,40 T 50,40 T 70,40 T 90,40" stroke="#FFF" stroke-width="4" fill="transparent"/></svg>` },
{ id: 'naranja_fruta', colorId: 'naranja', svg: `<svg viewBox="0 0 100 100"><circle cx="50" cy="55" r="35" fill="#FF9500"/><circle cx="50" cy="20" r="4" fill="#8D6E63"/><path fill="#4CAF50" d="M50,20 C60,5 75,10 70,25 C60,20 50,20 50,20 Z"/></svg>` }
];
// SVGs para Juego 5 (Colorear) - Usamos la clase 'fillable' para las áreas que se pueden pintar
const SVGS_COLORING = [
// Casa
`<svg viewBox="0 0 100 100">
<polygon class="fillable" fill="#FFF" points="10,50 50,10 90,50 90,90 10,90" />
<polygon class="fillable" fill="#FFF" points="5,50 50,5 95,50" />
<rect class="fillable" fill="#FFF" x="40" y="60" width="20" height="30" />
<rect class="fillable" fill="#FFF" x="20" y="60" width="15" height="15" />
<rect class="fillable" fill="#FFF" x="65" y="60" width="15" height="15" />
</svg>`,
// Flor
`<svg viewBox="0 0 100 100">
<circle class="fillable" fill="#FFF" cx="50" cy="30" r="15" />
<circle class="fillable" fill="#FFF" cx="30" cy="50" r="15" />
<circle class="fillable" fill="#FFF" cx="70" cy="50" r="15" />
<circle class="fillable" fill="#FFF" cx="50" cy="70" r="15" />
<circle class="fillable" fill="#FFF" cx="50" cy="50" r="15" />
</svg>`,
// Coche
`<svg viewBox="0 0 100 100">
<path class="fillable" fill="#FFF" d="M 20 60 L 20 40 L 40 30 L 70 30 L 90 45 L 90 60 Z" />
<circle class="fillable" fill="#FFF" cx="35" cy="65" r="12" />
<circle class="fillable" fill="#FFF" cx="75" cy="65" r="12" />
<path class="fillable" fill="#FFF" d="M 45 32 L 65 32 L 65 42 L 40 42 Z" />
</svg>`
];
// Mensajes de motivación
const MOTIVATIONS = ["¡Muy bien!", "¡Genial!", "¡Fantástico!", "¡Eres un artista!", "¡Súper!", "¡Bravo!"];
/* =========================================================
SISTEMA DE AUDIO (Sintetizador sin archivos externos)
========================================================= */
const AudioEngine = {
ctx: null,
enabled: true,
init() {
if (!this.ctx) {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) {
this.ctx = new AudioContext();
}
}
// Si estaba suspendido (por políticas del navegador), reanudar
if (this.ctx && this.ctx.state === 'suspended') {
this.ctx.resume();
}
},
toggleSound() {
this.enabled = !this.enabled;
document.querySelectorAll('.sound-icon, #btn-sound-toggle').forEach(el => {
el.innerText = this.enabled ? '🔊' : '🔇';
});
if (this.enabled) this.init();
},
playTone(freq, type, duration, vol=0.1) {
if (!this.enabled || !this.ctx) return;
try {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
gain.gain.setValueAtTime(vol, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + duration);
} catch (e) { console.warn("Audio error", e); }
},
success() {
this.playTone(523.25, 'sine', 0.1, 0.2); // C5
setTimeout(() => this.playTone(659.25, 'sine', 0.2, 0.2), 100); // E5
},
error() {
this.playTone(200, 'triangle', 0.3, 0.2);
},
pop() {
this.playTone(800, 'sine', 0.05, 0.1);
},
cheer() {
if (!this.enabled || !this.ctx) return;
// Efecto de arpegio rápido simulando alegría
const notes = [440, 554, 659, 880];
notes.forEach((freq, i) => {
setTimeout(() => this.playTone(freq, 'square', 0.1, 0.1), i * 80);
});
}
};
/* =========================================================
LÓGICA CENTRAL DE LA APLICACIÓN
========================================================= */
const App = {
currentScreen: 'menu',
targetGame: null,
difficulty: 'easy',
colorsPool: [],
streak: 0,
init() {
// Prevenir zoom por doble tap en iOS
document.addEventListener('dblclick', function(event) {
event.preventDefault();
}, { passive: false });
},
setScreen(screenId) {
// Iniciar audio en la primera interacción del usuario
AudioEngine.init();
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById('screen-' + screenId).classList.add('active');
this.currentScreen = screenId;
// Limpiar juegos al salir
if(screenId === 'menu') {
Games.G4.stopLoop();
}
},
showDifficulty(gameId) {
// El juego 5 (colorear) no necesita nivel de dificultad
if (gameId === 'g5') {
this.targetGame = gameId;
this.startGame('easy'); // Nivel por defecto, no importa
return;
}
this.targetGame = gameId;
this.setScreen('difficulty');
},
startGame(diff) {
this.difficulty = diff;
this.streak = 0;
// Seleccionar pool de colores según dificultad
if (diff === 'easy') this.colorsPool = COLORS_DATA.slice(0, 4); // Rojo, Azul, Amarillo, Verde
else if (diff === 'medium') this.colorsPool = COLORS_DATA.slice(0, 8); // + Naranja, Morado, Rosa, Marrón
else this.colorsPool = COLORS_DATA.slice(0, 11); // Todos
this.setScreen(this.targetGame);
// Iniciar el juego correspondiente
if (this.targetGame === 'g1') Games.G1.init();
else if (this.targetGame === 'g2') Games.G2.init();
else if (this.targetGame === 'g3') Games.G3.init();
else if (this.targetGame === 'g4') Games.G4.init();
else if (this.targetGame === 'g5') Games.G5.init();
},
// Utilidad para obtener N elementos aleatorios
getRandomItems(arr, count) {
const shuffled = [...arr].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
},
// Sistema de motivación (cada 5 aciertos seguidos en minijuegos)
addSuccess() {
this.streak++;
AudioEngine.success();
if (this.streak > 0 && this.streak % 5 === 0) {
this.showMotivation();
}
},
resetStreak() {
this.streak = 0;
AudioEngine.error();
},
showMotivation() {
AudioEngine.cheer();
const overlay = document.getElementById('motivation-overlay');
const text = document.getElementById('motivation-text');
text.innerText = this.getRandomItems(MOTIVATIONS, 1)[0];
// Crear confeti
overlay.innerHTML = '';
overlay.appendChild(text);
for(let i=0; i<30; i++) {
const conf = document.createElement('div');
conf.className = 'confetti';
conf.style.left = Math.random() * 100 + 'vw';
conf.style.backgroundColor = COLORS_DATA[Math.floor(Math.random()*COLORS_DATA.length)].hex;
conf.style.animationDuration = (Math.random() * 2 + 2) + 's';
conf.style.animationDelay = (Math.random() * 1) + 's';
overlay.appendChild(conf);
}
overlay.classList.add('show');
setTimeout(() => {
overlay.classList.remove('show');
}, 2500);
}
};
/* =========================================================
JUEGOS
========================================================= */
const Games = {
// --- JUEGO 1: Encuentra el color ---
G1: {
score: 0,
targetColor: null,
init() {
this.score = 0;
this.updateScore();
this.nextRound();
},
nextRound() {
const container = document.getElementById('g1-circles');
container.innerHTML = '';
// Número de opciones basadas en dificultad (4 a 8)
let numOptions = App.difficulty === 'easy' ? 4 : (App.difficulty === 'medium' ? 6 : 8);
// Elegir color objetivo
this.targetColor = App.getRandomItems(App.colorsPool, 1)[0];
document.getElementById('g1-instruction').innerText = `Toca el color ${this.targetColor.name.toUpperCase()}`;
// Elegir colores de relleno (sin repetir el objetivo)
let others = App.colorsPool.filter(c => c.id !== this.targetColor.id);
// Rellenar si faltan colores en el pool actual (para hard/med)
while (others.length < numOptions - 1) {
others = others.concat(COLORS_DATA.filter(c => c.id !== this.targetColor.id));
}
let options = [this.targetColor, ...App.getRandomItems(others, numOptions - 1)];
options = App.getRandomItems(options, options.length); // Mezclar
options.forEach(color => {
const circle = document.createElement('div');
circle.className = 'color-circle';
circle.style.backgroundColor = color.hex;
circle.onclick = () => this.checkAnswer(color.id, circle);
container.appendChild(circle);
});
},
checkAnswer(id, el) {
if (id === this.targetColor.id) {
this.score++;
this.updateScore();
App.addSuccess();
// Pequeña animación de salida
el.style.transform = 'scale(1.5)';
el.style.opacity = '0';
setTimeout(() => this.nextRound(), 400);
} else {
App.resetStreak();
el.classList.add('wrong-shake');
setTimeout(() => el.classList.remove('wrong-shake'), 400);
}
},
updateScore() {
document.getElementById('g1-score').innerText = this.score;
}
},
// --- JUEGO 2: ¿De qué color es? ---
G2: {
score: 0,
targetSvg: null,
init() {
this.score = 0;
this.updateScore();
this.nextRound();
},
nextRound() {
const drawing = document.getElementById('g2-drawing');
const optionsContainer = document.getElementById('g2-options');
optionsContainer.innerHTML = '';
// Limitar SVGs a los colores del pool actual
const validSvgs = SVGS_COLORED.filter(s => App.colorsPool.find(c => c.id === s.colorId));
// Fallback de seguridad si no hay suficientes
this.targetSvg = App.getRandomItems(validSvgs.length > 0 ? validSvgs : SVGS_COLORED, 1)[0];
// Mostrar SVG
drawing.innerHTML = this.targetSvg.svg;
// Buscar el objeto de color correcto
const correctColor = COLORS_DATA.find(c => c.id === this.targetSvg.colorId);
// Obtener opciones incorrectas
let others = App.colorsPool.filter(c => c.id !== correctColor.id);
let options = [correctColor, ...App.getRandomItems(others, 3)]; // Siempre 4 opciones
options = App.getRandomItems(options, options.length);
options.forEach(color => {
const btn = document.createElement('button');
btn.className = 'btn-large';
btn.style.backgroundColor = color.hex;
btn.style.width = '45%';
btn.style.maxWidth = '200px';
btn.style.padding = '15px';
// Ajustar contraste de texto
if (['blanco', 'amarillo'].includes(color.id)) {
btn.style.color = '#333';
btn.style.textShadow = 'none';
}
btn.innerText = color.name.toUpperCase();
btn.onclick = () => this.checkAnswer(color.id, btn);
optionsContainer.appendChild(btn);
});
},
checkAnswer(id, el) {
if (id === this.targetSvg.colorId) {
this.score++;
this.updateScore();
App.addSuccess();
const svgEl = document.querySelector('#g2-drawing svg');
svgEl.style.transform = 'scale(1.2) rotate(10deg)';
setTimeout(() => this.nextRound(), 500);
} else {
App.resetStreak();
el.classList.add('wrong-shake');
setTimeout(() => el.classList.remove('wrong-shake'), 400);
}
},
updateScore() {
document.getElementById('g2-score').innerText = this.score;
}
},
// --- JUEGO 3: Memoria de colores ---
G3: {
moves: 0,
pairs: 0,
cards: [],
flippedCards: [],
locked: false,
totalPairs: 0,
init() {
this.moves = 0;
this.pairs = 0;
this.flippedCards = [];
this.locked = false;
this.updateStats();
const board = document.getElementById('g3-board');
board.innerHTML = '';
// Dificultad: Easy(4 pares), Med(6 pares), Hard(8 pares)
this.totalPairs = App.difficulty === 'easy' ? 4 : (App.difficulty === 'medium' ? 6 : 8);
// Ajustar Grid
let cols = this.totalPairs === 4 ? 4 : 4;
board.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
// Elegir colores
let selectedColors = App.getRandomItems(App.colorsPool, this.totalPairs);
// Duplicar para crear pares
this.cards = [...selectedColors, ...selectedColors];
this.cards = App.getRandomItems(this.cards, this.cards.length); // Shuffle
this.cards.forEach((color, index) => {
const card = document.createElement('div');
card.className = 'memory-card';
card.dataset.index = index;
card.dataset.colorId = color.id;
const front = document.createElement('div');
front.className = 'front';
front.style.backgroundColor = color.hex;
const back = document.createElement('div');
back.className = 'back';
card.appendChild(front);
card.appendChild(back);
card.onclick = () => this.flipCard(card);
board.appendChild(card);
});
},
flipCard(card) {
if (this.locked || card.classList.contains('flipped') || this.flippedCards.includes(card)) return;
AudioEngine.pop();
card.classList.add('flipped');
this.flippedCards.push(card);
if (this.flippedCards.length === 2) {
this.moves++;
this.updateStats();
this.checkMatch();
}
},
checkMatch() {
this.locked = true;
const [c1, c2] = this.flippedCards;
if (c1.dataset.colorId === c2.dataset.colorId) {
this.pairs++;
this.updateStats();
this.flippedCards = [];
this.locked = false;
App.addSuccess();
if (this.pairs === this.totalPairs) {
setTimeout(() => {
App.showMotivation(); // Gran celebración final
setTimeout(() => this.init(), 3000);
}, 500);
}
} else {
setTimeout(() => {
c1.classList.remove('flipped');
c2.classList.remove('flipped');
this.flippedCards = [];
this.locked = false;
AudioEngine.error();
}, 1000);
}
},
updateStats() {
document.getElementById('g3-moves').innerText = this.moves;
document.getElementById('g3-pairs').innerText = this.pairs;
}
},
// --- JUEGO 4: Explota el color ---
G4: {
targetColor: null,
remaining: 0,
spawnInterval: null,
container: null,
init() {
this.container = document.getElementById('g4-container');
this.container.innerHTML = '';
this.stopLoop();
this.startRound();
},
startRound() {
this.container.innerHTML = '';
this.targetColor = App.getRandomItems(App.colorsPool, 1)[0];
// Cantidad a explotar (5 a 10 según dificultad)
this.remaining = App.difficulty === 'easy' ? 5 : (App.difficulty === 'medium' ? 8 : 10);
document.getElementById('g4-instruction').innerText = `Explota las burbujas ${this.targetColor.name.toUpperCase()}`;
this.updateUI();
// Intervalo de aparición de burbujas
let speed = App.difficulty === 'easy' ? 1200 : (App.difficulty === 'medium' ? 800 : 500);
this.spawnInterval = setInterval(() => this.spawnBubble(), speed);
},
spawnBubble() {
if (App.currentScreen !== 'g4') {
this.stopLoop();
return;
}
const bubble = document.createElement('div');
bubble.className = 'bubble';
// Tamaño aleatorio (50px a 100px)
const size = 50 + Math.random() * 50;
bubble.style.width = size + 'px';
bubble.style.height = size + 'px';
// Posición horizontal
bubble.style.left = Math.random() * (window.innerWidth - size) + 'px';
// Duración de animación (flotar)
const duration = 4 + Math.random() * 4; // 4s a 8s
bubble.style.animationDuration = duration + 's';
// Color: % de que sea el correcto vs otros
let colorObj;
if (Math.random() < 0.4) {
colorObj = this.targetColor; // 40% correctas
} else {
colorObj = App.getRandomItems(App.colorsPool, 1)[0];
}
bubble.style.setProperty('--b-color', colorObj.hex);
bubble.dataset.colorId = colorObj.id;
bubble.onmousedown = (e) => this.popBubble(e, bubble, colorObj.id);
bubble.ontouchstart = (e) => { e.preventDefault(); this.popBubble(e, bubble, colorObj.id); };
// Limpieza cuando termine la animación
bubble.addEventListener('animationend', () => {
if(bubble.parentNode) bubble.parentNode.removeChild(bubble);
});
this.container.appendChild(bubble);
},
popBubble(e, bubble, id) {
if (bubble.classList.contains('popped')) return;
if (id === this.targetColor.id) {
// Correcto
AudioEngine.pop();
bubble.classList.add('popped');
this.remaining--;
this.updateUI();
App.addSuccess();
setTimeout(() => {
if(bubble.parentNode) bubble.parentNode.removeChild(bubble);
}, 200);
if (this.remaining <= 0) {
this.stopLoop();
setTimeout(() => this.startRound(), 2000); // Siguiente ronda
}
} else {
// Incorrecto
AudioEngine.error();
bubble.classList.add('wrong-shake');
}
},
updateUI() {
document.getElementById('g4-remaining').innerText = this.remaining;
},
stopLoop() {
if (this.spawnInterval) {
clearInterval(this.spawnInterval);
this.spawnInterval = null;
}
}
},
// --- JUEGO 5: Colorea ---
G5: {
selectedColor: '#FF3B30', // Default rojo
currentImageIndex: 0,
init() {
this.renderPalette();
this.loadImage();
},
renderPalette() {
const palette = document.getElementById('g5-palette');
palette.innerHTML = '';
// Usar todos los colores
COLORS_DATA.forEach((color, i) => {
const div = document.createElement('div');
div.className = 'palette-color';
if (i === 0) div.classList.add('selected');
div.style.backgroundColor = color.hex;
div.onclick = () => this.selectColor(color.hex, div);
palette.appendChild(div);
});
},
selectColor(hex, el) {
this.selectedColor = hex;
document.querySelectorAll('.palette-color').forEach(d => d.classList.remove('selected'));
el.classList.add('selected');
AudioEngine.pop();
},
loadImage() {
const container = document.getElementById('g5-canvas-container');
container.innerHTML = SVGS_COLORING[this.currentImageIndex];
// Añadir eventos a las partes rellenables
const fillables = container.querySelectorAll('.fillable');
fillables.forEach(el => {
el.onclick = () => {
el.style.fill = this.selectedColor;
AudioEngine.pop();
};
// Soporte táctil
el.ontouchstart = (e) => {
e.preventDefault();
el.style.fill = this.selectedColor;
AudioEngine.pop();
};
});
},
clearCanvas() {
const fillables = document.querySelectorAll('#g5-canvas-container .fillable');
fillables.forEach(el => el.style.fill = '#FFF');
AudioEngine.error(); // Sonido suave como "borrado"
},
nextImage() {
this.currentImageIndex = (this.currentImageIndex + 1) % SVGS_COLORING.length;
this.loadImage();
},
prevImage() {
this.currentImageIndex = (this.currentImageIndex - 1 + SVGS_COLORING.length) % SVGS_COLORING.length;
this.loadImage();
}
}
};
// Arrancar la app
window.onload = () => {
App.init();
};
</script>
</body>
</html>