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, minimum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Colores">
<meta name="theme-color" content="#FFCC33">
<meta name="format-detection" content="telephone=no">
<title>La aventura de los colores</title>
<style>
/* =====================================================
1. RESET Y VARIABLES GLOBALES
===================================================== */
*{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; user-select:none; -webkit-user-select:none; touch-action:manipulation; }
html,body{
margin:0; padding:0; width:100%; height:100%; overflow:hidden;
font-family:'Comic Sans MS','Chalkboard SE','Marker Felt','Trebuchet MS',sans-serif;
background:#FFF6E1;
-webkit-user-drag:none;
}
:root{
--rojo:#E8433D; --rojo-d:#C22A25;
--azul:#1E88E5; --azul-d:#125EA0;
--amarillo:#FFCC33; --amarillo-d:#E0A800;
--verde:#3DBE64; --verde-d:#289048;
--naranja:#FF8A3D; --naranja-d:#E06A1D;
--morado:#9C5FE0; --morado-d:#7538B5;
--cielo:#7EC8F2;
--crema:#FFF6E1;
--ink:#3A2E1F;
--white:#FFFFFF;
--safe-t:env(safe-area-inset-top,0px);
--safe-b:env(safe-area-inset-bottom,0px);
--safe-l:env(safe-area-inset-left,0px);
--safe-r:env(safe-area-inset-right,0px);
}
button{ font-family:inherit; border:none; cursor:pointer; }
svg{ display:block; pointer-events:none; }
/* =====================================================
2. LAYOUT GENERAL / PANTALLAS A PANTALLA COMPLETA
===================================================== */
#app{
position:fixed; inset:0; width:100%; height:100%;
padding-top:var(--safe-t); padding-bottom:var(--safe-b);
padding-left:var(--safe-l); padding-right:var(--safe-r);
}
.screen{
position:absolute; inset:0;
display:none;
flex-direction:column;
align-items:center; justify-content:center;
width:100%; height:100%;
padding:14px;
}
.screen.active{ display:flex; }
/* Barra superior común a las actividades */
.topbar{
position:absolute; top:calc(10px + var(--safe-t)); left:0; right:0;
display:flex; justify-content:space-between; align-items:flex-start;
padding:0 14px; z-index:20; pointer-events:none;
}
.topbar > *{ pointer-events:auto; }
.icon-btn{
width:64px; height:64px; min-width:64px; min-height:64px;
border-radius:50%;
background:var(--white);
box-shadow:0 4px 0 rgba(0,0,0,.15);
display:flex; align-items:center; justify-content:center;
transition:transform .1s;
}
.icon-btn:active{ transform:translateY(3px) scale(.95); box-shadow:0 1px 0 rgba(0,0,0,.15); }
.stars-bar{
display:flex; gap:4px; align-items:center;
background:var(--white); border-radius:40px; padding:8px 14px;
box-shadow:0 4px 0 rgba(0,0,0,.15);
}
.stars-bar svg{ width:26px; height:26px; }
/* =====================================================
3. PANTALLA DE MENU
===================================================== */
#screen-menu{ background:linear-gradient(160deg,#FFE9B0 0%, #FFF6E1 55%, #DFF3FF 100%); }
.menu-title{
font-size:clamp(28px,6vw,52px);
color:var(--ink);
text-shadow:2px 3px 0 rgba(255,255,255,.7);
text-align:center;
margin:0 0 6px 0;
font-weight:900;
}
.menu-mascot-wrap{ margin-bottom:6px; }
.menu-grid{
display:grid;
grid-template-columns:repeat(2,minmax(150px,220px));
gap:18px;
width:100%;
max-width:560px;
margin-top:8px;
}
.activity-card{
aspect-ratio:1/0.85;
border-radius:28px;
display:flex; flex-direction:column; align-items:center; justify-content:center;
gap:8px;
box-shadow:0 6px 0 rgba(0,0,0,.18);
transition:transform .12s;
padding:10px;
min-height:110px;
}
.activity-card:active{ transform:translateY(4px) scale(.97); box-shadow:0 2px 0 rgba(0,0,0,.18); }
.activity-card svg{ width:56px; height:56px; }
.activity-card span{ color:#fff; font-size:clamp(14px,2.6vw,20px); font-weight:900; text-shadow:1px 2px 0 rgba(0,0,0,.15); }
.card1{ background:linear-gradient(150deg,var(--rojo),var(--naranja)); }
.card2{ background:linear-gradient(150deg,var(--verde),#2BA5A0); }
.card3{ background:linear-gradient(150deg,var(--morado),#6B7BE0); }
.card4{ background:linear-gradient(150deg,var(--azul),#37B6D6); }
.menu-stars{
margin-top:14px;
display:flex; gap:8px; align-items:center;
background:rgba(255,255,255,.85);
padding:8px 18px; border-radius:40px;
box-shadow:0 4px 0 rgba(0,0,0,.1);
}
.menu-stars svg{ width:30px; height:30px; }
/* =====================================================
4. MASCOTA (SOLETE) - guia animada
===================================================== */
.mascot{
width:clamp(80px,16vw,140px); height:clamp(80px,16vw,140px);
animation:float 3s ease-in-out infinite;
cursor:pointer;
}
@keyframes float{ 0%,100%{transform:translateY(0) rotate(-2deg);} 50%{transform:translateY(-10px) rotate(2deg);} }
.mascot.bounce{ animation:bounceHappy .6s ease; }
@keyframes bounceHappy{
0%{transform:scale(1);} 30%{transform:scale(1.25) rotate(-8deg);} 60%{transform:scale(.9) rotate(6deg);} 100%{transform:scale(1) rotate(0);}
}
.speech-bubble{
position:relative;
background:#fff; border-radius:20px; padding:10px 18px;
box-shadow:0 4px 0 rgba(0,0,0,.12);
font-size:clamp(16px,3vw,24px); font-weight:900; color:var(--ink);
max-width:80vw; text-align:center;
margin-top:6px;
}
/* =====================================================
5. BARRA INFERIOR: volver a intentar / menu (activities)
===================================================== */
.bottombar{
position:absolute; bottom:calc(10px + var(--safe-b)); left:0; right:0;
display:flex; justify-content:center; gap:16px;
z-index:20;
}
.pill-btn{
display:flex; align-items:center; gap:8px;
background:var(--white); border-radius:40px;
padding:12px 20px; min-height:60px;
box-shadow:0 4px 0 rgba(0,0,0,.15);
font-size:clamp(14px,2.6vw,18px); font-weight:900; color:var(--ink);
}
.pill-btn:active{ transform:translateY(3px); box-shadow:0 1px 0 rgba(0,0,0,.15); }
.pill-btn svg{ width:28px; height:28px; }
/* =====================================================
6. ACTIVIDAD 1: RECONOCER EL COLOR
===================================================== */
#screen-a1{ background:linear-gradient(160deg,#FFF0D0,#FFF6E1); }
.a1-target-label{
margin-top:calc(70px + var(--safe-t));
font-size:clamp(20px,4vw,30px); font-weight:900; color:var(--ink);
}
.a1-options{
display:grid; grid-template-columns:repeat(2,1fr); gap:22px;
width:100%; max-width:520px; margin-top:14px;
flex:1; align-content:center;
}
.color-blob{
width:100%; aspect-ratio:1/1; max-height:150px;
border-radius:40% 60% 55% 45%/50% 45% 55% 50%;
box-shadow:0 6px 0 rgba(0,0,0,.18);
transition:transform .12s;
min-width:80px; min-height:80px;
}
.color-blob:active{ transform:scale(.94); }
.color-blob.wrong-shake{ animation:shakeSoft .5s; }
.color-blob.correct-pop{ animation:popCorrect .5s; }
@keyframes shakeSoft{ 0%,100%{transform:translateX(0);} 20%{transform:translateX(-10px);} 40%{transform:translateX(10px);} 60%{transform:translateX(-6px);} 80%{transform:translateX(6px);} }
@keyframes popCorrect{ 0%{transform:scale(1);} 40%{transform:scale(1.3);} 100%{transform:scale(1);} }
/* =====================================================
7. ACTIVIDAD 2: ASOCIAR OBJETOS Y COLORES
===================================================== */
#screen-a2{ background:linear-gradient(160deg,#E7FBEF,#FFF6E1); }
.a2-wrap{ width:100%; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; padding-top:calc(60px + var(--safe-t)); padding-bottom:70px; }
.a2-circles{
display:flex; justify-content:center; gap:14px; flex-wrap:wrap;
margin-bottom:10px;
}
.a2-circle{
width:74px; height:74px; border-radius:50%;
border:5px dashed rgba(255,255,255,.8);
box-shadow:inset 0 0 0 3px rgba(0,0,0,.08);
display:flex; align-items:center; justify-content:center;
transition:transform .15s;
}
.a2-circle.hover-target{ transform:scale(1.15); border-style:solid; }
.a2-circle.filled{ border-style:solid; }
.a2-objects{
display:flex; justify-content:center; gap:16px; flex-wrap:wrap;
max-width:600px; margin-top:10px;
}
.a2-object{
width:66px; height:66px;
touch-action:none;
cursor:grab;
filter:drop-shadow(0 4px 2px rgba(0,0,0,.2));
transition:opacity .2s;
}
.a2-object.dragging{ position:fixed; z-index:999; pointer-events:none; }
.a2-object.placed{ opacity:0; pointer-events:none; }
.a2-object.selected{ transform:scale(1.15); filter:drop-shadow(0 0 0 4px rgba(255,255,255,.9)) drop-shadow(0 4px 2px rgba(0,0,0,.2)); }
/* =====================================================
8. ACTIVIDAD 3: MEZCLAR COLORES
===================================================== */
#screen-a3{ background:linear-gradient(160deg,#F1E9FF,#FFF6E1); }
.a3-wrap{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:26px; width:100%; padding-top:calc(50px + var(--safe-t)); }
.a3-drops{ display:flex; align-items:center; gap:26px; }
.color-drop{
width:100px; height:100px;
border-radius:50% 50% 50% 0;
transform:rotate(45deg);
box-shadow:0 6px 0 rgba(0,0,0,.18);
transition:transform .15s;
}
.color-drop:active{ transform:rotate(45deg) scale(.9); }
.color-drop.selected{ transform:rotate(45deg) scale(1.2); box-shadow:0 0 0 6px rgba(255,255,255,.9),0 6px 0 rgba(0,0,0,.18); }
.a3-plus{ font-size:42px; font-weight:900; color:var(--ink); }
.a3-result-zone{
width:150px; height:150px; border-radius:50%;
border:6px dashed #cbb; background:rgba(255,255,255,.5);
display:flex; align-items:center; justify-content:center;
transition:background .4s;
}
.a3-result-zone.filled{ border-style:solid; }
.a3-result-zone svg{ width:70px; height:70px; }
/* =====================================================
9. ACTIVIDAD 4: MEMORIA DE COLORES
===================================================== */
#screen-a4{ background:linear-gradient(160deg,#E8F3FF,#FFF6E1); }
.a4-wrap{ width:100%; height:100%; display:flex; align-items:center; justify-content:center; padding-top:calc(60px + var(--safe-t)); padding-bottom:70px; }
.a4-grid{
display:grid;
grid-template-columns:repeat(4,minmax(60px,84px));
grid-auto-rows:minmax(60px,84px);
gap:10px;
}
.a4-card{
width:100%; height:100%;
border-radius:16px;
position:relative;
transform-style:preserve-3d;
transition:transform .4s;
cursor:pointer;
}
.a4-card.flipped{ transform:rotateY(180deg); }
.a4-card.matched{ opacity:.35; }
.a4-face{
position:absolute; inset:0; border-radius:16px;
display:flex; align-items:center; justify-content:center;
backface-visibility:hidden;
box-shadow:0 4px 0 rgba(0,0,0,.15);
}
.a4-back{ background:linear-gradient(150deg,#FFD23F,#FF8A3D); }
.a4-back svg{ width:34px; height:34px; }
.a4-front{ transform:rotateY(180deg); }
.a4-front svg{ width:38px; height:38px; }
/* =====================================================
10. PANTALLA DE CELEBRACION
===================================================== */
#screen-win{ background:radial-gradient(circle at 50% 30%,#FFF7DE,#FFE29A); overflow:hidden; }
.win-face{ width:clamp(140px,30vw,220px); animation:bounceHappy 1s ease infinite; }
.win-text{ font-size:clamp(28px,6vw,48px); font-weight:900; color:var(--ink); text-align:center; margin:10px 0; text-shadow:2px 3px 0 rgba(255,255,255,.8); }
.win-stars{ display:flex; gap:10px; margin:10px 0 20px; }
.win-stars svg{ width:44px; height:44px; }
.confetti{
position:absolute; top:-20px; width:14px; height:14px; border-radius:3px;
animation:fall linear forwards;
}
@keyframes fall{ to{ transform:translateY(115vh) rotate(360deg); opacity:.9; } }
/* =====================================================
11. UTILIDADES / RESPONSIVE
===================================================== */
.hidden{ display:none !important; }
@media (max-width:420px){
.menu-grid{ grid-template-columns:repeat(2,minmax(120px,1fr)); gap:12px; }
.a2-object{ width:56px; height:56px; }
.color-drop{ width:80px; height:80px; }
.a3-result-zone{ width:120px; height:120px; }
}
@media (orientation:landscape) and (max-height:480px){
.menu-title{ font-size:22px; margin-bottom:2px;}
.menu-mascot-wrap .mascot{ width:56px; height:56px; }
.menu-grid{ grid-template-columns:repeat(4,minmax(90px,140px)); }
.activity-card svg{ width:38px; height:38px; }
.a1-target-label{ margin-top:56px; font-size:18px; }
.a4-grid{ grid-template-columns:repeat(6,minmax(46px,64px)); grid-auto-rows:minmax(46px,64px); }
}
@media (min-width:900px){
.menu-grid{ grid-template-columns:repeat(4,180px); max-width:820px; }
.a1-options{ max-width:640px; }
}
</style>
</head>
<body>
<div id="app">
<!-- ============ MENU PRINCIPAL ============ -->
<div class="screen active" id="screen-menu">
<div class="menu-mascot-wrap">
<svg class="mascot" id="mascot-menu" viewBox="0 0 120 120"></svg>
</div>
<h1 class="menu-title">La aventura<br>de los colores</h1>
<div class="menu-grid">
<button class="activity-card card1" data-go="a1" aria-label="Reconoce el color">
<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="34" fill="#fff"/><circle cx="50" cy="50" r="34" fill="none" stroke="#fff" stroke-width="6"/><circle cx="50" cy="50" r="14" fill="#FF8A3D"/></svg>
<span>Reconoce</span>
</button>
<button class="activity-card card2" data-go="a2" aria-label="Asocia objetos">
<svg viewBox="0 0 100 100"><circle cx="30" cy="60" r="18" fill="#fff"/><path d="M60 30 q10 -14 22 -4 q10 10 -4 20 l-20 22 z" fill="#fff"/></svg>
<span>Asocia</span>
</button>
<button class="activity-card card3" data-go="a3" aria-label="Mezcla colores">
<svg viewBox="0 0 100 100"><path d="M50 15 C35 40 25 50 25 65 a25 25 0 0 0 50 0 C75 50 65 40 50 15 Z" fill="#fff"/></svg>
<span>Mezcla</span>
</button>
<button class="activity-card card4" data-go="a4" aria-label="Memoria de colores">
<svg viewBox="0 0 100 100"><rect x="18" y="18" width="28" height="36" rx="6" fill="#fff"/><rect x="54" y="46" width="28" height="36" rx="6" fill="#fff" opacity=".8"/></svg>
<span>Memoria</span>
</button>
</div>
<div class="menu-stars" id="menu-stars-bar"></div>
</div>
<!-- ============ ACTIVIDAD 1: RECONOCE EL COLOR ============ -->
<div class="screen" id="screen-a1">
<div class="topbar">
<button class="icon-btn" data-go="menu" aria-label="Menú principal">
<svg viewBox="0 0 24 24"><path d="M4 11 L12 4 L20 11" fill="none" stroke="#FF8A3D" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><rect x="6" y="11" width="12" height="9" rx="1.5" fill="none" stroke="#FF8A3D" stroke-width="2.6"/></svg>
</button>
<div class="stars-bar" id="a1-progress"></div>
<button class="icon-btn" id="a1-repeat" aria-label="Repetir">
<svg viewBox="0 0 24 24"><path d="M4 4 v6 h6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 20 v-6 h-6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15 a9 9 0 0 0 15 4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/><path d="M19 9 a9 9 0 0 0 -15 -4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/></svg>
</button>
</div>
<svg class="mascot" id="mascot-a1" viewBox="0 0 120 120" style="width:70px;height:70px;"></svg>
<div class="a1-target-label" id="a1-label"> </div>
<div class="a1-options" id="a1-options"></div>
</div>
<!-- ============ ACTIVIDAD 2: ASOCIA OBJETOS Y COLORES ============ -->
<div class="screen" id="screen-a2">
<div class="topbar">
<button class="icon-btn" data-go="menu" aria-label="Menú principal">
<svg viewBox="0 0 24 24"><path d="M4 11 L12 4 L20 11" fill="none" stroke="#3DBE64" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><rect x="6" y="11" width="12" height="9" rx="1.5" fill="none" stroke="#3DBE64" stroke-width="2.6"/></svg>
</button>
<div class="stars-bar" id="a2-progress"></div>
<button class="icon-btn" id="a2-repeat" aria-label="Repetir">
<svg viewBox="0 0 24 24"><path d="M4 4 v6 h6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 20 v-6 h-6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15 a9 9 0 0 0 15 4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/><path d="M19 9 a9 9 0 0 0 -15 -4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/></svg>
</button>
</div>
<div class="a2-wrap">
<div class="a2-circles" id="a2-circles"></div>
<div class="a2-objects" id="a2-objects"></div>
</div>
</div>
<!-- ============ ACTIVIDAD 3: MEZCLA DE COLORES ============ -->
<div class="screen" id="screen-a3">
<div class="topbar">
<button class="icon-btn" data-go="menu" aria-label="Menú principal">
<svg viewBox="0 0 24 24"><path d="M4 11 L12 4 L20 11" fill="none" stroke="#9C5FE0" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><rect x="6" y="11" width="12" height="9" rx="1.5" fill="none" stroke="#9C5FE0" stroke-width="2.6"/></svg>
</button>
<div class="stars-bar" id="a3-progress"></div>
<button class="icon-btn" id="a3-repeat" aria-label="Repetir">
<svg viewBox="0 0 24 24"><path d="M4 4 v6 h6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 20 v-6 h-6" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15 a9 9 0 0 0 15 4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/><path d="M19 9 a9 9 0 0 0 -15 -4" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round"/></svg>
</button>
</div>
<div class="a3-wrap">
<div class="a1-target-label" id="a3-label" style="margin-top:0;"> </div>
<div class="a3-drops" id="a3-drops"></div>
<div class="a3-result-zone" id="a3-result"></div>
</div>
</div>
<!-- ============ ACTIVIDAD 4: MEMORIA DE COLORES ============ -->
<div class="screen" id="screen-a4">
<div class="topbar">
<button class="icon-btn" data-go="menu" aria-label="Menú principal">
<svg viewBox="0 0 24 24"><path d="M4 11 L12 4 L20 11" fill="none" stroke="#1E88E5" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><rect x="6" y="11" width="12" height="9" rx="1.5" fill="none" stroke="#1E88E5" stroke-width="2.6"/></svg>
</button>
<div class="stars-bar" id="a4-progress"></div>
<button class="icon-btn" id="a4-restart" aria-label="Volver a intentar">
<svg viewBox="0 0 24 24"><path d="M4 4 v6 h6" fill="none" stroke="#E8433D" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 20 v-6 h-6" fill="none" stroke="#E8433D" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15 a9 9 0 0 0 15 4" fill="none" stroke="#E8433D" stroke-width="2.6" stroke-linecap="round"/><path d="M19 9 a9 9 0 0 0 -15 -4" fill="none" stroke="#E8433D" stroke-width="2.6" stroke-linecap="round"/></svg>
</button>
</div>
<div class="a4-wrap">
<div class="a4-grid" id="a4-grid"></div>
</div>
</div>
<!-- ============ PANTALLA DE CELEBRACION ============ -->
<div class="screen" id="screen-win">
<svg class="win-face" id="win-face" viewBox="0 0 120 120"></svg>
<div class="win-text">¡Genial!</div>
<div class="win-stars" id="win-stars"></div>
<div class="bottombar" style="position:static;">
<button class="pill-btn" id="win-retry">
<svg viewBox="0 0 24 24"><path d="M4 4 v6 h6" fill="none" stroke="#3DBE64" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 20 v-6 h-6" fill="none" stroke="#3DBE64" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 15 a9 9 0 0 0 15 4" fill="none" stroke="#3DBE64" stroke-width="2.6" stroke-linecap="round"/><path d="M19 9 a9 9 0 0 0 -15 -4" fill="none" stroke="#3DBE64" stroke-width="2.6" stroke-linecap="round"/></svg>
Otra vez
</button>
<button class="pill-btn" id="win-menu">
<svg viewBox="0 0 24 24"><path d="M4 11 L12 4 L20 11" fill="none" stroke="#FF8A3D" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/><rect x="6" y="11" width="12" height="9" rx="1.5" fill="none" stroke="#FF8A3D" stroke-width="2.6"/></svg>
Menú
</button>
</div>
</div>
</div>
<script>
/* =====================================================
0. DATOS GLOBALES: colores, objetos, mezclas
===================================================== */
var COLORS = [
{id:'rojo', name:'Rojo', hex:'#E8433D', dark:'#C22A25'},
{id:'azul', name:'Azul', hex:'#1E88E5', dark:'#125EA0'},
{id:'amarillo', name:'Amarillo', hex:'#FFCC33', dark:'#E0A800'},
{id:'verde', name:'Verde', hex:'#3DBE64', dark:'#289048'},
{id:'naranja', name:'Naranja', hex:'#FF8A3D', dark:'#E06A1D'},
{id:'morado', name:'Morado', hex:'#9C5FE0', dark:'#7538B5'}
];
function colorById(id){ for(var i=0;i<COLORS.length;i++){ if(COLORS[i].id===id) return COLORS[i]; } return null; }
var MIXES = [
{a:'azul', b:'amarillo', result:'verde'},
{a:'rojo', b:'azul', result:'morado'},
{a:'rojo', b:'amarillo', result:'naranja'}
];
/* Iconos SVG simples para objetos cotidianos (paths internos, sin viewBox propio) */
var OBJECT_ICONS = {
manzana: '<path d="M50 25 c-4 -10 -18 -12 -14 0 c-16 -4 -28 12 -22 30 c5 15 16 27 28 27 c3 0 5 -1 8 -1 c3 0 5 1 8 1 c12 0 23 -12 28 -27 c6 -18 -6 -34 -22 -30 c4 -12 -10 -10 -14 0 Z" fill="{c}"/><rect x="47" y="14" width="6" height="12" rx="3" fill="#6B4A2B" transform="rotate(20 50 20)"/>',
platano: '<path d="M30 75 C20 55 30 30 55 22 C60 20 63 25 59 28 C42 36 34 55 42 70 C46 78 36 84 30 75 Z" fill="{c}"/>',
cielo: '<circle cx="50" cy="45" r="26" fill="{c}"/><ellipse cx="34" cy="60" rx="16" ry="10" fill="{c}"/><ellipse cx="66" cy="60" rx="16" ry="10" fill="{c}"/>',
hierba: '<path d="M30 80 Q30 40 40 20 Q42 45 42 80 Z" fill="{c}"/><path d="M50 80 Q48 30 60 15 Q56 45 58 80 Z" fill="{c}"/><path d="M68 80 Q70 42 80 25 Q76 50 76 80 Z" fill="{c}"/>',
naranjafruta: '<circle cx="50" cy="52" r="30" fill="{c}"/><rect x="47" y="18" width="6" height="10" rx="3" fill="#3DBE64"/>',
uvas: '<circle cx="38" cy="45" r="12" fill="{c}"/><circle cx="58" cy="45" r="12" fill="{c}"/><circle cx="48" cy="60" r="12" fill="{c}"/><circle cx="38" cy="70" r="12" fill="{c}"/><circle cx="58" cy="70" r="12" fill="{c}"/><path d="M48 30 q0 -12 10 -16" fill="none" stroke="#3DBE64" stroke-width="4" stroke-linecap="round"/>'
};
var OBJECTS = [
{id:'manzana', name:'Manzana', colorId:'rojo'},
{id:'platano', name:'Plátano', colorId:'amarillo'},
{id:'cielo', name:'Cielo', colorId:'azul'},
{id:'hierba', name:'Hierba', colorId:'verde'},
{id:'naranjafruta', name:'Naranja', colorId:'naranja'},
{id:'uvas', name:'Uvas', colorId:'morado'}
];
function objectIconSVG(objId, hex){
var raw = OBJECT_ICONS[objId].split('{c}').join(hex);
return '<svg viewBox="0 0 100 100">'+raw+'</svg>';
}
/* =====================================================
1. UTILIDADES: voz, sonido, estrellas, navegacion
===================================================== */
function speak(text){
try{
if(!('speechSynthesis' in window)) return;
window.speechSynthesis.cancel();
var u = new SpeechSynthesisUtterance(text);
u.lang = 'es-ES';
u.rate = 0.85;
u.pitch = 1.15;
var voices = window.speechSynthesis.getVoices();
for(var i=0;i<voices.length;i++){
if(voices[i].lang && voices[i].lang.toLowerCase().indexOf('es')===0){ u.voice = voices[i]; break; }
}
window.speechSynthesis.speak(u);
}catch(e){}
}
// Cargar voces (algunos navegadores las cargan async)
if('speechSynthesis' in window){
window.speechSynthesis.onvoiceschanged = function(){};
}
var audioCtx = null;
function getAudioCtx(){
if(!audioCtx){
try{ audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }catch(e){}
}
return audioCtx;
}
function playTone(freq, duration, delay, type){
var ctx = getAudioCtx();
if(!ctx) return;
delay = delay || 0;
var t0 = ctx.currentTime + delay;
var osc = ctx.createOscillator();
var gain = ctx.createGain();
osc.type = type || 'sine';
osc.frequency.setValueAtTime(freq, t0);
gain.gain.setValueAtTime(0.0001, t0);
gain.gain.exponentialRampToValueAtTime(0.28, t0 + 0.02);
gain.gain.exponentialRampToValueAtTime(0.0001, t0 + duration);
osc.connect(gain); gain.connect(ctx.destination);
osc.start(t0); osc.stop(t0 + duration + 0.02);
}
function soundCorrect(){ playTone(660,0.14,0,'sine'); playTone(880,0.18,0.12,'sine'); }
function soundWrong(){ playTone(200,0.25,0,'sawtooth'); }
function soundCheer(){
var notes=[523,659,784,1046];
for(var i=0;i<notes.length;i++){ playTone(notes[i],0.22,i*0.13,'triangle'); }
}
function unlockAudio(){
var ctx = getAudioCtx();
if(ctx && ctx.state === 'suspended'){ ctx.resume(); }
}
document.addEventListener('pointerdown', unlockAudio, {once:true});
/* Estrellas / progreso guardado localmente (sin conexion) */
var STORAGE_KEY = 'coloresApp_progreso';
function loadProgress(){
try{
var raw = localStorage.getItem(STORAGE_KEY);
if(raw) return JSON.parse(raw);
}catch(e){}
return {a1:false,a2:false,a3:false,a4:false};
}
function saveProgress(p){
try{ localStorage.setItem(STORAGE_KEY, JSON.stringify(p)); }catch(e){}
}
var progress = loadProgress();
function starCount(){
var n=0; for(var k in progress){ if(progress[k]) n++; } return n;
}
function starSVG(filled){
var fill = filled ? '#FFCC33' : '#E3DACB';
return '<svg viewBox="0 0 24 24"><path d="M12 2 L14.7 9 L22 9.5 L16.5 14.3 L18.2 21.5 L12 17.6 L5.8 21.5 L7.5 14.3 L2 9.5 L9.3 9 Z" fill="'+fill+'"/></svg>';
}
function renderMenuStars(){
var el = document.getElementById('menu-stars-bar');
var html = '';
for(var i=0;i<4;i++){ html += starSVG(i < starCount()); }
el.innerHTML = html;
}
function renderMiniStars(containerId, current, total){
var el = document.getElementById(containerId);
if(!el) return;
var html = '';
for(var i=0;i<total;i++){ html += starSVG(i < current); }
el.innerHTML = html;
}
/* Navegacion entre pantallas */
var screens = ['menu','a1','a2','a3','a4','win'];
var currentScreen = 'menu';
function goTo(name){
currentScreen = name;
for(var i=0;i<screens.length;i++){
var el = document.getElementById('screen-'+screens[i]);
if(screens[i]===name) el.classList.add('active'); else el.classList.remove('active');
}
if(name==='menu'){ renderMenuStars(); animateMascotMenu(); }
if(name==='a1') startActivity1();
if(name==='a2') startActivity2();
if(name==='a3') startActivity3();
if(name==='a4') startActivity4();
}
document.body.addEventListener('click', function(e){
var t = e.target.closest('[data-go]');
if(t){ goTo(t.getAttribute('data-go')); }
});
/* =====================================================
2. MASCOTA (SOLETE - un sol sonriente animado)
===================================================== */
function mascotSVG(mood){
// mood: 'happy' (normal), 'wink', 'excited'
var mouth = mood==='excited'
? '<path d="M42 78 Q60 96 78 78" stroke="#C22A25" stroke-width="4" fill="#fff" stroke-linecap="round"/>'
: '<path d="M45 76 Q60 88 75 76" stroke="#C22A25" stroke-width="4" fill="none" stroke-linecap="round"/>';
var eyeR = mood==='wink'
? '<path d="M70 58 q7 -6 14 0" stroke="#3A2E1F" stroke-width="4" fill="none" stroke-linecap="round"/>'
: '<circle cx="77" cy="58" r="5" fill="#3A2E1F"/>';
var rays = '';
for(var i=0;i<8;i++){
var ang = i*45;
rays += '<rect x="56" y="2" width="8" height="16" rx="4" fill="#FFCC33" transform="rotate('+ang+' 60 60)"/>';
}
return '<svg viewBox="0 0 120 120">'+
rays+
'<circle cx="60" cy="60" r="34" fill="#FFCC33"/>'+
'<circle cx="60" cy="60" r="34" fill="none" stroke="#E0A800" stroke-width="3"/>'+
'<circle cx="43" cy="58" r="5" fill="#3A2E1F"/>'+
eyeR+
'<circle cx="38" cy="70" r="6" fill="#FF8A3D" opacity=".6"/>'+
'<circle cx="82" cy="70" r="6" fill="#FF8A3D" opacity=".6"/>'+
mouth+
'</svg>';
}
function animateMascotMenu(){
document.getElementById('mascot-menu').innerHTML = mascotSVG('happy');
}
document.getElementById('mascot-menu').innerHTML = mascotSVG('happy');
document.getElementById('mascot-menu').addEventListener('click', function(){
bounceMascot(this);
speak('¡Hola! Toca una actividad para aprender los colores.');
});
function bounceMascot(el){
el.classList.remove('bounce'); void el.offsetWidth; el.classList.add('bounce');
}
/* =====================================================
3. ACTIVIDAD 1: RECONOCE EL COLOR
===================================================== */
var a1Round = 0;
var a1Total = 5;
var a1Target = null;
var a1Busy = false;
function startActivity1(){
a1Round = 0;
document.getElementById('mascot-a1').innerHTML = mascotSVG('happy');
renderMiniStars('a1-progress', 0, a1Total);
nextA1Round();
}
function nextA1Round(){
if(a1Round >= a1Total){ return winScreen('a1'); }
a1Busy = false;
var shuffled = COLORS.slice().sort(function(){return Math.random()-0.5;});
var numOptions = 4;
var options = shuffled.slice(0, numOptions);
a1Target = options[Math.floor(Math.random()*options.length)];
document.getElementById('a1-label').textContent = '';
var optWrap = document.getElementById('a1-options');
optWrap.innerHTML = '';
options.sort(function(){return Math.random()-0.5;});
for(var i=0;i<options.length;i++){
(function(c){
var blob = document.createElement('div');
blob.className = 'color-blob';
blob.style.background = c.hex;
blob.setAttribute('aria-label', c.name);
blob.addEventListener('click', function(){ handleA1Choice(c, blob); });
optWrap.appendChild(blob);
})(options[i]);
}
setTimeout(function(){
speak('Toca el color ' + a1Target.name);
bounceMascot(document.getElementById('mascot-a1'));
}, 350);
}
function handleA1Choice(c, el){
if(a1Busy) return;
if(c.id === a1Target.id){
a1Busy = true;
el.classList.add('correct-pop');
soundCorrect();
speak('¡Muy bien! ' + c.name);
a1Round++;
renderMiniStars('a1-progress', a1Round, a1Total);
setTimeout(nextA1Round, 900);
} else {
el.classList.add('wrong-shake');
soundWrong();
setTimeout(function(){ el.classList.remove('wrong-shake'); }, 500);
}
}
document.getElementById('a1-repeat').addEventListener('click', function(){
if(a1Target) speak('Toca el color ' + a1Target.name);
bounceMascot(document.getElementById('mascot-a1'));
});
/* =====================================================
4. ACTIVIDAD 2: ASOCIA OBJETOS Y COLORES (arrastrar)
===================================================== */
var a2Placed = 0;
var a2Objects = [];
var a2Selected = null;
function startActivity2(){
a2Placed = 0;
a2Selected = null;
renderMiniStars('a2-progress', 0, OBJECTS.length);
a2Objects = OBJECTS.slice().sort(function(){return Math.random()-0.5;});
var circlesEl = document.getElementById('a2-circles');
circlesEl.innerHTML = '';
var circleOrder = COLORS.slice().sort(function(){return Math.random()-0.5;});
for(var i=0;i<circleOrder.length;i++){
var c = circleOrder[i];
var circ = document.createElement('div');
circ.className = 'a2-circle';
circ.style.background = 'rgba(255,255,255,.35)';
circ.style.borderColor = c.hex;
circ.dataset.colorId = c.id;
circ.style.setProperty('--fill', c.hex);
circ.addEventListener('click', function(){ onA2CircleTap(this); });
circlesEl.appendChild(circ);
dyeCircleBg(circ, c.hex, false);
}
var objsEl = document.getElementById('a2-objects');
objsEl.innerHTML = '';
for(var j=0;j<a2Objects.length;j++){
var o = a2Objects[j];
var col = colorById(o.colorId);
var wrap = document.createElement('div');
wrap.className = 'a2-object';
wrap.dataset.objId = o.id;
wrap.dataset.colorId = o.colorId;
wrap.innerHTML = objectIconSVG(o.id, col.hex);
attachA2Drag(wrap);
wrap.addEventListener('click', function(){ onA2ObjectTap(this); });
objsEl.appendChild(wrap);
}
setTimeout(function(){
speak('Arrastra cada cosa hasta su color.');
}, 300);
}
function dyeCircleBg(circ, hex, on){
circ.style.background = on ? hex : 'rgba(255,255,255,.35)';
}
function onA2ObjectTap(el){
if(el.classList.contains('placed')) return;
document.querySelectorAll('.a2-object.selected').forEach(function(o){ o.classList.remove('selected'); });
el.classList.add('selected');
a2Selected = el;
var o = OBJECTS.find(function(x){return x.id===el.dataset.objId;});
if(o) speak(o.name);
}
function onA2CircleTap(circ){
if(!a2Selected) return;
checkA2Match(a2Selected, circ);
}
function checkA2Match(objEl, circ){
if(objEl.dataset.colorId === circ.dataset.colorId){
objEl.classList.remove('selected');
objEl.classList.add('placed');
a2Selected = null;
dyeCircleBg(circ, circ.style.borderColor, true);
circ.classList.add('filled');
soundCorrect();
var col = colorById(circ.dataset.colorId);
speak('¡Sí! Es de color ' + col.name);
a2Placed++;
renderMiniStars('a2-progress', a2Placed, OBJECTS.length);
if(a2Placed >= OBJECTS.length){ setTimeout(function(){ winScreen('a2'); }, 700); }
} else {
soundWrong();
circ.style.transform = 'scale(0.9)';
setTimeout(function(){ circ.style.transform=''; },200);
}
}
function attachA2Drag(el){
var startX=0, startY=0, origX=0, origY=0, dragging=false, moved=false;
el.addEventListener('pointerdown', function(e){
if(el.classList.contains('placed')) return;
dragging = true; moved = false;
var rect = el.getBoundingClientRect();
origX = rect.left; origY = rect.top;
startX = e.clientX; startY = e.clientY;
el.setPointerCapture(e.pointerId);
el.style.width = rect.width+'px'; el.style.height = rect.height+'px';
});
el.addEventListener('pointermove', function(e){
if(!dragging) return;
var dx = e.clientX - startX, dy = e.clientY - startY;
if(Math.abs(dx)>6 || Math.abs(dy)>6){
moved = true;
if(!el.classList.contains('dragging')){
el.classList.add('dragging');
el.style.left = origX+'px'; el.style.top = origY+'px';
}
el.style.left = (origX+dx)+'px';
el.style.top = (origY+dy)+'px';
}
});
el.addEventListener('pointerup', function(e){
dragging = false;
if(!moved){ return; } // sera tratado como click (tap-to-select)
el.classList.remove('dragging');
el.style.left=''; el.style.top=''; el.style.width=''; el.style.height='';
var elAt = document.elementFromPoint(e.clientX, e.clientY);
var circ = elAt ? elAt.closest('.a2-circle') : null;
if(circ){ checkA2Match(el, circ); }
});
}
document.getElementById('a2-repeat').addEventListener('click', function(){
speak('Arrastra cada cosa hasta su color.');
});
/* =====================================================
5. ACTIVIDAD 3: MEZCLAR COLORES
===================================================== */
var a3Round = 0;
var a3Selected = [];
var a3Busy = false;
function startActivity3(){
a3Round = 0;
renderMiniStars('a3-progress', 0, MIXES.length);
nextA3Round();
}
function nextA3Round(){
if(a3Round >= MIXES.length){ return winScreen('a3'); }
a3Busy = false;
a3Selected = [];
var mix = MIXES[a3Round];
var colA = colorById(mix.a), colB = colorById(mix.b);
document.getElementById('a3-label').textContent = '';
document.getElementById('a3-result').classList.remove('filled');
document.getElementById('a3-result').style.background = 'rgba(255,255,255,.5)';
document.getElementById('a3-result').innerHTML = '';
var dropsEl = document.getElementById('a3-drops');
dropsEl.innerHTML = '';
var order = [colA, colB].sort(function(){return Math.random()-0.5;});
order.forEach(function(c){
var d = document.createElement('div');
d.className = 'color-drop';
d.style.background = c.hex;
d.dataset.colorId = c.id;
d.addEventListener('click', function(){ onA3DropTap(d, mix); });
dropsEl.appendChild(d);
});
setTimeout(function(){
speak('Toca ' + colA.name + ' y ' + colB.name + ' para mezclarlos.');
}, 300);
}
function onA3DropTap(el, mix){
if(a3Busy) return;
if(el.classList.contains('selected')){
el.classList.remove('selected');
a3Selected = a3Selected.filter(function(x){return x!==el;});
return;
}
el.classList.add('selected');
a3Selected.push(el);
soundTapMini();
if(a3Selected.length === 2){
a3Busy = true;
var ids = a3Selected.map(function(x){return x.dataset.colorId;}).sort();
var need = [mix.a, mix.b].sort();
if(ids[0]===need[0] && ids[1]===need[1]){
setTimeout(function(){ revealMix(mix); }, 350);
} else {
soundWrong();
setTimeout(function(){
a3Selected.forEach(function(x){ x.classList.remove('selected'); });
a3Selected = [];
a3Busy = false;
}, 500);
}
}
}
function soundTapMini(){ playTone(440,0.08,0,'sine'); }
function revealMix(mix){
var resultCol = colorById(mix.result);
var zone = document.getElementById('a3-result');
zone.classList.add('filled');
zone.style.background = resultCol.hex;
soundCorrect();
speak('¡' + colorById(mix.a).name + ' más ' + colorById(mix.b).name + ' es igual a ' + resultCol.name + '!');
a3Round++;
renderMiniStars('a3-progress', a3Round, MIXES.length);
setTimeout(nextA3Round, 1600);
}
document.getElementById('a3-repeat').addEventListener('click', function(){
if(a3Round < MIXES.length){
var mix = MIXES[a3Round];
speak('Toca ' + colorById(mix.a).name + ' y ' + colorById(mix.b).name + ' para mezclarlos.');
}
});
/* =====================================================
6. ACTIVIDAD 4: MEMORIA DE COLORES
===================================================== */
var a4Cards = [];
var a4First = null, a4Second = null, a4Busy = false, a4MatchedCount = 0;
var a4PairsTotal = 6;
function startActivity4(){
a4MatchedCount = 0; a4First = null; a4Second = null; a4Busy = false;
renderMiniStars('a4-progress', 0, a4PairsTotal);
var chosen = COLORS.slice(0, a4PairsTotal);
var deck = [];
chosen.forEach(function(c){ deck.push({colorId:c.id}); deck.push({colorId:c.id}); });
deck.sort(function(){return Math.random()-0.5;});
a4Cards = deck;
var grid = document.getElementById('a4-grid');
grid.innerHTML = '';
deck.forEach(function(card, idx){
var col = colorById(card.colorId);
var cardEl = document.createElement('div');
cardEl.className = 'a4-card';
cardEl.dataset.idx = idx;
cardEl.innerHTML =
'<div class="a4-face a4-back"><svg viewBox="0 0 24 24"><path d="M12 2 L14.7 9 L22 9.5 L16.5 14.3 L18.2 21.5 L12 17.6 L5.8 21.5 L7.5 14.3 L2 9.5 L9.3 9 Z" fill="#fff"/></svg></div>'+
'<div class="a4-face a4-front" style="background:'+col.hex+'"></div>';
cardEl.addEventListener('click', function(){ onA4CardTap(cardEl, card); });
grid.appendChild(cardEl);
});
setTimeout(function(){ speak('Encuentra las parejas de colores.'); }, 300);
}
function onA4CardTap(el, card){
if(a4Busy) return;
if(el.classList.contains('flipped') || el.classList.contains('matched')) return;
el.classList.add('flipped');
soundTapMini();
if(!a4First){
a4First = {el:el, card:card};
return;
}
a4Second = {el:el, card:card};
a4Busy = true;
if(a4First.card.colorId === a4Second.card.colorId){
soundCorrect();
var colName = colorById(card.colorId).name;
speak(colName);
setTimeout(function(){
a4First.el.classList.add('matched');
a4Second.el.classList.add('matched');
a4MatchedCount++;
renderMiniStars('a4-progress', a4MatchedCount, a4PairsTotal);
a4First=null; a4Second=null; a4Busy=false;
if(a4MatchedCount >= a4PairsTotal){ setTimeout(function(){ winScreen('a4'); }, 500); }
}, 500);
} else {
soundWrong();
setTimeout(function(){
a4First.el.classList.remove('flipped');
a4Second.el.classList.remove('flipped');
a4First=null; a4Second=null; a4Busy=false;
}, 800);
}
}
document.getElementById('a4-restart').addEventListener('click', function(){ startActivity4(); });
/* =====================================================
7. PANTALLA DE CELEBRACION
===================================================== */
var lastActivity = null;
function winScreen(activityId){
lastActivity = activityId;
progress[activityId] = true;
saveProgress(progress);
document.getElementById('win-face').innerHTML = mascotSVG('excited');
var html = '';
for(var i=0;i<4;i++){ html += starSVG(i < starCount()); }
document.getElementById('win-stars').innerHTML = html;
goTo('win');
spawnConfetti();
soundCheer();
setTimeout(function(){ speak('¡Muy bien! Lo has hecho genial.'); }, 300);
}
function spawnConfetti(){
var host = document.getElementById('screen-win');
var old = host.querySelectorAll('.confetti');
old.forEach(function(o){ o.remove(); });
var palette = COLORS.map(function(c){return c.hex;});
for(var i=0;i<45;i++){
var d = document.createElement('div');
d.className = 'confetti';
d.style.left = (Math.random()*100)+'%';
d.style.background = palette[Math.floor(Math.random()*palette.length)];
d.style.animationDuration = (2 + Math.random()*2.2)+'s';
d.style.animationDelay = (Math.random()*0.6)+'s';
d.style.borderRadius = (Math.random()>0.5? '50%':'3px');
host.appendChild(d);
(function(node){
setTimeout(function(){ node.remove(); }, 5200);
})(d);
}
}
document.getElementById('win-retry').addEventListener('click', function(){
if(lastActivity) goTo(lastActivity);
});
document.getElementById('win-menu').addEventListener('click', function(){
goTo('menu');
});
/* =====================================================
8. INICIO
===================================================== */
renderMenuStars();
/* Prevenir el gesto de pellizco/zoom accidental y doble-tap zoom */
document.addEventListener('gesturestart', function(e){ e.preventDefault(); });
var lastTouchEnd = 0;
document.addEventListener('touchend', function(e){
var now = Date.now();
if(now - lastTouchEnd <= 300){ e.preventDefault(); }
lastTouchEnd = now;
}, {passive:false});
</script>
</body>
</html>