first commit

This commit is contained in:
Eidan T.
2026-05-09 12:17:01 -04:00
commit cb00aed531
12 changed files with 890 additions and 0 deletions
+548
View File
@@ -0,0 +1,548 @@
# Juego de Memoria - HTML, CSS y JavaScript
Juego de memoria interactivo desarrollado únicamente con HTML, CSS y JavaScript puro.
El proyecto incluye:
- Sistema de cartas con animaciones
- Barajado automático
- Pantalla completa
- Confetti al ganar
- Diseño responsive
- Sistema de comparación de pares
---
# Tecnologías Utilizadas
- HTML5
- CSS3
- JavaScript Vanilla
- Canvas Confetti (CDN)
---
# Estructura del Proyecto
```txt
/
├── index.html
├── style.css
├── script.js
├── fullscreen-icon.png
└── images/
├── back.png
├── img-1.png
├── img-2.png
├── img-3.png
├── img-4.png
├── img-5.png
└── img-6.png
```
---
# Estructura HTML Principal
## Contenedor del Juego
```html
<div class="wrapper"></div>
```
---
## Tablero de Cartas
```html
<ul class="cards"></ul>
```
---
## Carta Individual
```html
<li class="card"></li>
```
---
## Frente de la Carta
```html
<div class="view front-view">
<img src="images/back.png" />
</div>
```
---
## Dorso de la Carta
```html
<div class="view back-view">
<img src="images/img-N.png" />
</div>
```
---
## Botón de Pantalla Completa
```html
<button id="fullscreen-btn">
<img src="fullscreen-icon.png" />
</button>
```
---
# Funciones Principales
El archivo `script.js` contiene las funciones principales del juego.
| Función | Descripción |
| -------------------------- | ---------------------------------- |
| `shuffleCard()` | Baraja y reinicia las cartas |
| `flipCard()` | Voltea una carta |
| `matchCards()` | Compara dos cartas |
| `toggleFullScreen()` | Activa/desactiva pantalla completa |
| `mostrarConfetiConTexto()` | Muestra confetti al ganar |
---
# Instalación
## Opción 1 — Abrir Directamente
1. Descomprimir el proyecto
2. Abrir `index.html`
Ideal para pruebas rápidas.
---
## Opción 2 — Localhost con XAMPP
Copiar el proyecto a:
```txt
C:\xampp\htdocs\version3x4\
```
Iniciar Apache y abrir:
```txt
http://localhost/version3x4/
```
---
## Opción 3 — Node.js
Instalar `serve`:
```bash
npm i -g serve
```
Ejecutar:
```bash
serve .
```
---
## Opción 4 — Producción Apache
### Crear carpeta
```bash
sudo mkdir -p /var/www/version3x4
```
### Copiar archivos
```bash
sudo cp -r ./Version3x4/* /var/www/version3x4/
```
### Permisos
```bash
sudo chown -R www-data:www-data /var/www/version3x4
```
### VirtualHost
```apache
<VirtualHost *:80>
ServerName juego.midominio.com
DocumentRoot /var/www/version3x4
<Directory /var/www/version3x4>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
```
### Activar sitio
```bash
sudo a2ensite juego.midominio.com
sudo systemctl reload apache2
```
---
## Opción 5 — Producción Nginx
```nginx
server {
server_name juego.midominio.com;
root /var/www/version3x4;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
Reiniciar Nginx:
```bash
sudo systemctl reload nginx
```
---
# Funcionamiento del Juego
## Tablero
- Formato 3×4
- 12 cartas
- 6 pares
---
## Mecánica
### Volteo
Cuando el usuario hace click:
- se ejecuta `flipCard()`
- la carta gira
---
### Comparación
`matchCards()` compara:
```javascript
.back-view img
```
---
### Acierto
Si coinciden:
- permanecen abiertas
- aumenta contador `matched`
---
### Fallo
Si no coinciden:
- se aplica `.shake`
- se espera un tiempo
- las cartas se vuelven a tapar
---
### Victoria
Cuando todos los pares son encontrados:
```javascript
mostrarConfetiConTexto("...");
```
---
### Pantalla Completa
El botón:
```html
#fullscreen-btn
```
ejecuta:
```javascript
toggleFullScreen();
```
---
# Ediciones Frecuentes
## Cambiar Imágenes
Reemplazar:
```txt
images/img-1.png
images/img-2.png
...
```
manteniendo:
- nombres
- formato
---
## Cambiar Reverso
Modificar:
```txt
images/back.png
```
---
## Cambiar Layout 3×4 a 4×4
### Agregar más cartas
Duplicar:
```html
<li class="card"></li>
```
---
### Agregar nuevas imágenes
```txt
images/img-7.png
images/img-8.png
```
---
### Cambiar Grid CSS
```css
.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
```
---
# Ajustar Dificultad
## Velocidad de Animación
Modificar:
```css
.card .view
```
---
## Sacudida de Error
Editar:
```css
.card.shake
```
y:
```css
@keyframes shake;
```
---
## Tiempo de Error
Modificar `setTimeout()` dentro de `script.js`.
---
# Cambiar Mensaje de Victoria
Editar:
```javascript
mostrarConfetiConTexto("Mensaje");
```
---
# Pantalla Completa
La función utiliza:
```javascript
document.documentElement.requestFullscreen();
```
y:
```javascript
document.exitFullscreen();
```
---
# Checklist de Pruebas
## Validaciones Básicas
- [ ] Las 12 cartas aparecen
- [ ] Las cartas giran
- [ ] Los pares permanecen abiertos
- [ ] El shake funciona
- [ ] El tablero se baraja
- [ ] El fullscreen funciona
- [ ] Responsive correcto
---
# Problemas Comunes
| Problema | Causa | Solución |
| -------------------- | --------------- | ------------------- |
| Cartas blancas | Ruta incorrecta | Revisar `/images` |
| No funciona el juego | Error JS | Revisar consola F12 |
| No aparece confetti | CDN bloqueado | Revisar conexión |
| Fullscreen falla | Sin interacción | Ejecutar tras click |
| Responsive roto | Grid incorrecto | Ajustar CSS |
---
# Mejoras Opcionales
## Contador de Movimientos
Agregar:
```html
<div id="moves"></div>
```
---
## Temporizador
Agregar:
```html
<div id="time"></div>
```
---
## Botón Reiniciar
Crear botón que llame:
```javascript
shuffleCard();
```
---
## Sonidos
Usar:
```javascript
HTMLAudioElement;
```
---
## Mejor Tiempo
Guardar datos con:
```javascript
localStorage;
```
---
# Guía Express de Edición
## Cambiar imágenes
Reemplazar:
```txt
images/img-*.png
```
---
## Añadir más pares
1. Duplicar cartas
2. Añadir nuevas imágenes
3. Ajustar grid CSS
---
## Cambiar dificultad
Modificar:
- `setTimeout()`
- animaciones CSS
---
## Cambiar texto de victoria
Editar:
```javascript
mostrarConfetiConTexto();
```
---
# Características Técnicas
- No utiliza backend
- No requiere base de datos
- Todo funciona en el navegador
- Proyecto completamente frontend
- Compatible con hosting estático
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

+103
View File
@@ -0,0 +1,103 @@
<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Juego De Memoria - ePromo</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="wrapper">
<ul class="cards">
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-1.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-2.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-3.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-4.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-5.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-6.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-7.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-8.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-9.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-10.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-1.png" alt="card-img">
</div> </li>
<li class="card">
<div class="view front-view">
<img src="images/back.png" alt="card-img">
</div>
<div class="view back-view">
<img src="images/img-2.png" alt="card-img">
</div> </li>
</ul>
</div>
<button id="fullscreen-btn">
<img src="fullscreen-icon.png" alt="Pantalla Completa">
</button>
<script src="script.js"></script>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js"></script>
</body></html>
+109
View File
@@ -0,0 +1,109 @@
const cards = document.querySelectorAll(".card");
let matched = 0;
let cardOne, cardTwo;
let disableDeck = false;
function flipCard({target: clickedCard}) {
if(cardOne !== clickedCard && !disableDeck) {
clickedCard.classList.add("flip");
if(!cardOne) {
return cardOne = clickedCard;
}
cardTwo = clickedCard;
disableDeck = true;
let cardOneImg = cardOne.querySelector(".back-view img").src,
cardTwoImg = cardTwo.querySelector(".back-view img").src;
matchCards(cardOneImg, cardTwoImg);
}
}
function matchCards(img1, img2) {
if(img1 === img2) {
matched++;
if(matched == 6) {
setTimeout(() => {
mostrarConfetiConTexto();
shuffleCard();
}, 1000);
}
cardOne.removeEventListener("click", flipCard);
cardTwo.removeEventListener("click", flipCard);
cardOne = cardTwo = "";
return disableDeck = false;
}
setTimeout(() => {
cardOne.classList.add("shake");
cardTwo.classList.add("shake");
}, 400);
setTimeout(() => {
cardOne.classList.remove("shake", "flip");
cardTwo.classList.remove("shake", "flip");
cardOne = cardTwo = "";
disableDeck = false;
}, 1200);
}
function shuffleCard() {
matched = 0;
disableDeck = false;
cardOne = cardTwo = "";
let arr = [1,1,2,2,3,3,4,4,5,5,6,6];
arr.sort(() => Math.random() > 0.5 ? 1 : -1);
cards.forEach((card, i) => {
card.classList.remove("flip");
let imgTag = card.querySelector(".back-view img");
imgTag.src = `images/img-${arr[i]}.png`;
card.addEventListener("click", flipCard);
});
}
shuffleCard();
cards.forEach(card => {
card.addEventListener("click", flipCard);
});
function toggleFullScreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => {
console.log(`Error al intentar activar pantalla completa: ${err.message}`);
});
} else {
document.exitFullscreen();
}
}
document.addEventListener("DOMContentLoaded", function () {
document.getElementById("fullscreen-btn").addEventListener("click", toggleFullScreen);
});
// Mostrar confeti con texto
function mostrarConfetiConTexto(texto) {
const duration = 5 * 1000;
const end = Date.now() + duration;
(function frame() {
confetti({
particleCount: 7,
angle: 60,
spread: 55,
origin: { x: 0 },
scalar: 1.2,
});
confetti({
particleCount: 7,
angle: 120,
spread: 55,
origin: { x: 1 },
scalar: 1.2,
});
if (Date.now() < end) {
requestAnimationFrame(frame);
}
})();
console.log("¡Felicidades! Ganaste: " + texto);
}
+130
View File
@@ -0,0 +1,130 @@
/* Import Google Font - Poppins */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #E9AD14 /*url(https://portal.gomezleemarketing.com/wp-content/uploads/2024/02/BANNER-SUPERIOR-GRANDE.png) repeat center center*/;
}
.wrapper{
padding: 10px;
border-radius: 10px;
background: #727272;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.cards, .card, .view{
display: flex;
align-items: center;
justify-content: center;
}
.cards{
height: 800px;
width: 800px;
flex-wrap: wrap;
justify-content: space-between;
}
.cards .card{
cursor: pointer;
list-style: none;
user-select: none;
position: relative;
perspective: 1000px;
transform-style: preserve-3d;
height: calc(100% / 4 - 10px);
width: calc(100% / 3 - 10px);
}
.card.shake{
animation: shake 0.35s ease-in-out;
}
@keyframes shake {
0%, 100%{
transform: translateX(0);
}
20%{
transform: translateX(-13px);
}
40%{
transform: translateX(13px);
}
60%{
transform: translateX(-8px);
}
80%{
transform: translateX(8px);
}
}
.card .view{
width: 100%;
height: 100%;
position: absolute;
border-radius: 7px;
background: #fff;
pointer-events: none;
backface-visibility: hidden;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
transition: transform 0.25s linear;
}
.card .front-view img{
width: 80px;
}
.card .back-view img{
max-width: 150px;
}
.card .back-view{
transform: rotateY(-180deg);
}
.card.flip .back-view{
transform: rotateY(0);
}
.card.flip .front-view{
transform: rotateY(180deg);
}
@media screen and (max-width: 700px) {
.cards{
height: 400px;
width: 400px;
}
.card .front-view img{
width: 45px;
}
.card .back-view img{
max-width: 80px;
}
}
@media screen and (max-width: 530px) {
.cards{
height: 400px;
width: 400px;
}
.card .front-view img{
width: 45px;
}
.card .back-view img{
max-width: 80px;
}
}
#fullscreen-btn {
position: fixed;
top: 10px;
right: 10px;
background: none;
border: none;
cursor: pointer;
z-index: 1000;
}
#fullscreen-btn img {
width: 40px;
height: 40px;
}