distribution animation

This commit is contained in:
Didictateur 2026-03-14 11:18:29 +01:00
parent 662ee8fd10
commit 8d04e99a85
2 changed files with 151 additions and 14 deletions

View file

@ -39,6 +39,13 @@ class Hand {
});
}
shuffle() {
for (let i = this.tiles.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];
}
}
flatten() {
if (this.drawn) {
this.tiles.push(this.drawn);
@ -107,7 +114,7 @@ class Discard {
}
class Game {
constructor() {
constructor(skipInitialDeal = false) {
this.discards = [
new Discard(),
new Discard(),
@ -115,20 +122,33 @@ class Game {
new Discard()
];
this.wall = new Draw();
this.hands = [
this.wall.drawHand(),
this.wall.drawHand(),
this.wall.drawHand(),
this.wall.drawHand()
];
for (let hand of this.hands) {
hand.sort();
if (skipInitialDeal) {
this.hands = [
new Hand(),
new Hand(),
new Hand(),
new Hand()
];
} else {
this.hands = [
this.wall.drawHand(),
this.wall.drawHand(),
this.wall.drawHand(),
this.wall.drawHand()
];
for (let hand of this.hands) {
hand.sort();
}
}
this.firstDealer = Math.floor(Math.random() * 4) * 0;
this.turn = this.firstDealer;
this.repeat = 0;
this.draw(this.turn);
if (!skipInitialDeal) {
this.draw(this.turn);
}
}
newDeal(hasRepeat = false) {

View file

@ -344,14 +344,15 @@
startButton.addEventListener('click', () => {
startScreen.style.display = 'none';
gameCenter.style.display = 'flex';
// Instancier une nouvelle partie et afficher les mains
game = new Game();
renderHands();
updateTurnIndicator();
// Instancier une nouvelle partie avec mains vides pour distribution animée
game = new Game(true);
// Lancer l'animation de distribution
distributeHands();
});
// Déclarer les variables et fonctions ici pour qu'elles soient accessibles partout
let game;
let isDistributing = false;
// Charger le menu
fetch('/components/menu.html')
.then(r => r.text())
@ -373,6 +374,119 @@
// Instancier une nouvelle partie et afficher les mains
async function distributeHands() {
isDistributing = true;
// Distribution 4 par 4, puis 1 par 1 comme dans la vraie vie
// 3 rounds de 4 tuiles par joueur = 12 tuiles
for (let round = 0; round < 3; round++) {
for (let player = 0; player < 4; player++) {
for (let i = 0; i < 4; i++) {
game.hands[player].tiles.push(game.wall.drawTile());
}
renderHands();
await new Promise(resolve => setTimeout(resolve, 300));
}
}
// Distribution finale : 1 par 1 pour la 13e tuile
for (let player = 0; player < 4; player++) {
game.hands[player].tiles.push(game.wall.drawTile());
renderHands();
await new Promise(resolve => setTimeout(resolve, 300));
}
// Trier les mains avec animation pour le joueur
const playerHandContainer = document.getElementById('hand-0').querySelector('.hand-tiles');
const playerTiles = game.hands[0].tiles;
const playerTileDivs = Array.from(playerHandContainer.children);
// Créer un map pour associer chaque tuile à son élément DOM
const tileToElement = new Map();
playerTileDivs.forEach((div, index) => {
tileToElement.set(playerTiles[index], div);
});
// Enregistrer les positions avant tri (en pixels relatifs au conteneur)
const originalPositions = new Map();
playerTileDivs.forEach((div, index) => {
originalPositions.set(div, index * (60 + 5)); // calcul basé sur width + gap
});
// Trier les mains
for (let hand of game.hands) {
hand.sort();
}
// Récupérer les nouvelles positions
const newPositions = new Map();
game.hands[0].tiles.forEach((tile, newIndex) => {
newPositions.set(tileToElement.get(tile), newIndex * (60 + 5));
});
// Appliquer une translation pour que les éléments restent à leur place visuelle
playerTileDivs.forEach(div => {
const originalX = originalPositions.get(div);
const newX = newPositions.get(div);
const deltaX = originalX - newX;
div.style.transform = `translateX(${deltaX}px)`;
div.style.transition = 'none';
});
// Forcer un reflow
void playerHandContainer.offsetWidth;
// Ré-ordonner les éléments dans le DOM selon le tri
const sortedDivs = game.hands[0].tiles.map(tile => tileToElement.get(tile));
sortedDivs.forEach(div => playerHandContainer.appendChild(div));
// Forcer un autre reflow
void playerHandContainer.offsetWidth;
// Animer vers les positions finales
playerTileDivs.forEach(div => {
div.style.transition = 'transform 0.6s ease-in-out';
div.style.transform = 'translateX(0)';
});
// Attendre la fin de l'animation
await new Promise(resolve => setTimeout(resolve, 600));
// Nettoyer les styles
playerTileDivs.forEach(div => {
div.style.transition = '';
div.style.transform = '';
});
// Re-rendre les bots sans animation
for (let i = 1; i < 4; i++) {
const botHandDiv = document.getElementById(`hand-${i}`);
const botHandTiles = document.createElement('div');
botHandTiles.className = 'hand-tiles';
for (let k = 0; k < game.hands[i].tiles.length; k++) {
const tileDiv = document.createElement('div');
tileDiv.innerHTML = `<svg class="tile tile-back" viewBox="0 0 310 410" height="82" width="60">
<image href="/img/Regular/Gray.svg" x="10" y="10" height="410" width="310"/>
<image href="/img/Regular/Back.svg" x="0" y="0" height="410" width="310"/>
</svg>`;
botHandTiles.appendChild(tileDiv);
}
const botHandWithDraw = botHandDiv.querySelector('.hand-with-draw') || botHandDiv;
const oldHandTiles = botHandWithDraw.querySelector('.hand-tiles');
if (oldHandTiles) oldHandTiles.remove();
botHandWithDraw.insertBefore(botHandTiles, botHandWithDraw.firstChild);
}
// Le joueur 0 pioche sa tuile supplémentaire
await new Promise(resolve => setTimeout(resolve, 500));
game.draw(game.turn);
renderHands();
updateTurnIndicator();
isDistributing = false;
}
function renderHands() {
for (let i = 0; i < 4; i++) {
const handDiv = document.getElementById(`hand-${i}`);
@ -397,6 +511,9 @@
const handleClick = (() => {
const tileToDiscard = tile;
return () => {
// Ne rien faire pendant la distribution
if (isDistributing) return;
// Trouver l'index actuel de la tuile
const currentIdx = game.hands[0].tiles.findIndex(t => t === tileToDiscard);
if (currentIdx !== -1) {