Animation via Canvas qui ne s'affiche pas
Lucryio
Messages postés
206
Date d'inscription
Statut
Membre
Dernière intervention
-
Lucryio Messages postés 206 Date d'inscription Statut Membre Dernière intervention -
Lucryio Messages postés 206 Date d'inscription Statut Membre Dernière intervention -
Bonjour à tous,
Je viens vers vous car, j'ai un souci avec mon script Canvas pour faire l'affichage dynamique de la météo de mon jeu en ligne sauf que les animations ne s'affiche pas.
Donc, les données sont bien récupéré (et correct) mais l'animation ne se fait pas peut importe la météo ou la période.
voici mon code complet de ma page (il est long je vous l'accord).
<?php require_once('../includes/session_controle.php'); require_once('../config/database.php'); require_once('../includes/header.php'); require_once('../includes/menu_gauche.php'); if (!isset($_SESSION['user_id'])) { header('Location: auth/login.php'); exit(); } $user_id = $_SESSION['user_id']; $stmt = $pdo->prepare("SELECT meteo, saison, periode, image FROM weather WHERE id = 1"); $stmt->execute(); $weather = $stmt->fetch(PDO::FETCH_ASSOC); if (!$weather) { $weather = [ 'meteo' => 'beau', 'saison' => 'printemps', 'periode' => 'jour', 'image' => '' ]; } $nomMap = 'nazaris'; if ($weather['saison'] === 'hiver') { $baseMapPath = __DIR__ . "/maps/hiver/$nomMap"; } else { $baseMapPath = __DIR__ . "/maps/$nomMap"; } $formats = ['png', 'gif']; $cheminMap = ''; foreach ($formats as $ext) { $fichierTest = "{$weather['periode']}.$ext"; $cheminComplet = "$baseMapPath/$fichierTest"; if (file_exists($cheminComplet)) { $cheminMap = $cheminComplet; break; } } if (!$cheminMap) { $fallbackPath1 = $weather['saison'] === 'hiver' ? __DIR__ . "/maps/hiver/$nomMap/$nomMap.png" : __DIR__ . "/maps/$nomMap/$nomMap.png"; if (file_exists($fallbackPath1)) { $cheminMap = $fallbackPath1; } else { $cheminMap = __DIR__ . "/maps/$nomMap/$nomMap.png"; } } $cheminMapUrl = str_replace(__DIR__, '.', $cheminMap); $mapIcones = [ 'beau' => 'wb_sunny', 'pluie' => 'umbrella', 'orage' => 'thunderstorm', 'neige' => 'ac_unit', 'brume' => 'blur_on', 'canicule' => 'wb_sunny', 'vent' => 'filter_drama', 'nuageux' => 'cloud', // <-- AJOUT ]; $iconeKey = $weather['meteo']; if (!isset($mapIcones[$iconeKey])) { $iconeKey = 'wb_sunny'; } $locale = 'fr_FR'; // Fixe le fuseau horaire pour toutes les fonctions date/time PHP date_default_timezone_set('Europe/Paris'); $formatter = new IntlDateFormatter( $locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, 'Europe/Paris', IntlDateFormatter::GREGORIAN, "EEEE d MMMM yyyy" ); $dateComplete = $formatter->format(new DateTime()); $heureAffichee = date('H:i'); $lieu = 'Nazaris'; if ($user_id) { $stmt = $pdo->prepare("SELECT lieu FROM users WHERE id = :id LIMIT 1"); $stmt->execute(['id' => $user_id]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result && !empty($result['lieu'])) { $lieu = $result['lieu']; } } $zoneDePecheCoords = [ '4,440,151,560', '151,453,365,560', '57,408,90,439', '183,376,232,457', '231,422,250,458', '248,439,360,467', '296,388,346,443', '361,467,376,558', '375,518,409,557', '408,532,425,558', ]; $zones = [ 'boutique_pokemon' => [ 'coords' => '417,104,429,117', 'href' => 'boutiquepokemon.php', 'alt' => 'Boutique Pokemon', 'clickable' => true, ], 'centre_pokemon' => [ 'coords' => '142,174,160,188', 'href' => 'centrepokemon.php', 'alt' => 'Centre Pokemon', 'clickable' => true, ], 'maison_dresseur' => [ 'coords' => '481,222,496,236', 'href' => 'maisondresseur.php', 'alt' => 'Maison du Dresseur', 'clickable' => true, ], 'laboratoire' => [ 'coords' => '289,175,304,188', 'href' => 'laboratoire.php', 'alt' => 'Laboratoire du Dr Maléfique', 'clickable' => true, ], 'route002' => [ 'coords' => '599,37,640,74', 'href' => 'route002.php', 'alt' => 'Vers la route002', 'clickable' => true, ], 'dresseurcarapuce' => [ 'coords' => '362,281,372,301', 'alt' => 'Carapuce, attaque pistolet à eau !!!', 'clickable' => false, ], 'carapuce' => [ 'coords' => '382,292,391,304', 'alt' => 'Caraaaaapu...bloupbloupbloup', 'clickable' => false, ], 'dresseurbulbizarre' => [ 'coords' => '471,280,483,303', 'alt' => 'Esquive et attaque fouet lianes', 'clickable' => false, ], 'bulbizarre' => [ 'coords' => '449,292,464,304', 'alt' => 'Bulbi bulbi bulbizzzarrrrrrddddd', 'clickable' => false, ], 'scientifique' => [ 'coords' => '334,180,348,202', 'alt' => 'Bienvenue au laboratoire du Dr Maléfique', 'clickable' => false, ], 'dresseurenhaut' => [ 'coords' => '530,95,542,117', 'alt' => 'Pfiouuuuu, le centre d\'entrainement est galère', 'clickable' => false, ], 'panneau' => [ 'coords' => '610,17,623,28', 'alt' => 'L\'aventure commence....', 'clickable' => false, ], 'enfantboueebleu' => [ 'coords' => '413,477,424,492', 'alt' => 'Jvais me prélacer ici', 'clickable' => false, ], 'enfantboueerose' => [ 'coords' => '128,406,139,424', 'alt' => 'Allons-y psykokwak, allons nous baigner', 'clickable' => false, ], 'psykokwak' => [ 'coords' => '107,408,117,423', 'alt' => 'Psyko...kwak ? aïe aië aïe aïe aïe....', 'clickable' => false, ], ]; // Ajout des zones de pêche dynamiquement foreach ($zoneDePecheCoords as $index => $coords) { $zones["zonedepeche_$index"] = [ 'coords' => $coords, 'href' => 'zonedepeche.php', // ou 'zonedepeche.php?zone=' . ($index + 1) 'alt' => 'Lancer ma canne', 'clickable' => true, ]; } ?> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link rel="stylesheet" href="assets/style.css" type="text/css" media="all"> <main> <div class="cadre-meteo" role="region" aria-label="Météo actuelle"> <span class="material-icons" aria-hidden="true"> <?= htmlspecialchars($mapIcones[$iconeKey] ?? 'wb_sunny') ?> </span> <div> Nous sommes le <strong><?= htmlspecialchars(ucfirst($dateComplete)) ?></strong>, il est <strong><?= htmlspecialchars($heureAffichee) ?></strong>.<br> <?= ucfirst(htmlspecialchars($weather['meteo'])) ?> (<?= ucfirst(htmlspecialchars($weather['saison'])) ?>, <?= ucfirst(htmlspecialchars($weather['periode'])) ?>) à <strong><?= htmlspecialchars($lieu) ?></strong>. </div> </div> <div class="map-container"> <img id="mapImage" src="<?= htmlspecialchars($cheminMapUrl) ?>" alt="Carte de la région <?= htmlspecialchars($nomMap) ?>"> <canvas id="mapCanvas"></canvas> <div id="zonesOverlay" ></div> </div> </main> <script> document.addEventListener('DOMContentLoaded', () => { const zones = <?= json_encode($zones, JSON_UNESCAPED_UNICODE); ?>; const mapImage = document.getElementById('mapImage'); const overlay = document.getElementById('zonesOverlay'); // Taille naturelle de l'image (taille originale de la map) const naturalWidth = 640; const naturalHeight = 560; // Fonction pour mettre à jour les zones interactives selon la taille affichée function updateZones() { const displayedWidth = mapImage.clientWidth; const displayedHeight = mapImage.clientHeight; const ratioX = displayedWidth / naturalWidth; const ratioY = displayedHeight / naturalHeight; overlay.innerHTML = ''; for (const key in zones) { const zone = zones[key]; const coordsArr = zone.coords.split(',').map(Number); const xs = []; const ys = []; for (let i = 0; i < coordsArr.length; i += 2) { xs.push(coordsArr[i]); ys.push(coordsArr[i + 1]); } const minX = Math.min(...xs); const maxX = Math.max(...xs); const minY = Math.min(...ys); const maxY = Math.max(...ys); const left = minX * ratioX; const top = minY * ratioY; const width = (maxX - minX) * ratioX; const height = (maxY - minY) * ratioY; const zoneDiv = document.createElement(zone.clickable ? 'a' : 'div'); if (zone.clickable) { zoneDiv.href = zone.href; zoneDiv.target = '_self'; zoneDiv.style.cursor = 'pointer'; } else { zoneDiv.style.cursor = 'default'; } zoneDiv.classList.add('zone', 'zone-tooltip'); zoneDiv.style.position = 'absolute'; zoneDiv.style.left = left + 'px'; zoneDiv.style.top = top + 'px'; zoneDiv.style.width = width + 'px'; zoneDiv.style.height = height + 'px'; zoneDiv.setAttribute('data-tooltip', zone.alt); // Fond semi-transparent pour visualiser la zone (à retirer en production si besoin) zoneDiv.style.backgroundColor = 'rgba(58, 134, 255, 0.3)'; zoneDiv.style.border = '1px solid #3a86ff'; overlay.appendChild(zoneDiv); } } updateZones(); window.addEventListener('resize', updateZones); // ================= // Canvas météo // ================= const canvas = document.getElementById('mapCanvas'); const ctx = canvas.getContext('2d'); // Resize canvas en fonction de la taille de l'image affichée function resizeCanvas() { const rect = mapImage.getBoundingClientRect(); canvas.style.width = rect.width + 'px'; canvas.style.height = rect.height + 'px'; canvas.width = rect.width * devicePixelRatio; canvas.height = rect.height * devicePixelRatio; ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.scale(devicePixelRatio, devicePixelRatio); } window.addEventListener('resize', () => { resizeCanvas(); if (currentAnimation) { currentAnimation.width = canvas.width / devicePixelRatio; currentAnimation.height = canvas.height / devicePixelRatio; } }); mapImage.addEventListener('load', resizeCanvas); resizeCanvas(); initWeatherCanvas(); // Utilitaires function random(min, max) { return Math.random() * (max - min) + min; } function lerp(a, b, t) { return a + (b - a) * t; } function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; } // Classe de base pour animations météo class WeatherAnimation { constructor(ctx, width, height) { this.ctx = ctx; this.width = width; this.height = height; this.particles = []; } start() {} stop() {} update(delta) {} draw() {} } // === BEAU === class BeauAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.birds = []; this.sunRotation = 0; this.createBirds(); this.particles = []; this.createParticles(); } createBirds() { for (let i = 0; i < 8; i++) { this.birds.push({ x: random(0, this.width), y: random(this.height * 0.15, this.height * 0.45), speed: random(20, 60), amplitude: random(10, 25), phase: random(0, Math.PI * 2), size: random(5, 10), }); } } createParticles() { for (let i = 0; i < 20; i++) { this.particles.push({ x: random(0, this.width), y: random(0, this.height * 0.6), radius: random(1, 3), alpha: random(0.3, 0.8), speedY: random(-5, 5), direction: Math.random() < 0.5 ? -1 : 1, speedX: random(2, 5), }); } } update(delta) { this.sunRotation += delta * 0.1; for (let b of this.birds) { b.x += b.speed * delta; b.y = this.height * 0.3 + Math.sin(b.phase + b.x * 0.02) * b.amplitude; if (b.x > this.width + 20) b.x = -20; } for (let p of this.particles) { p.x += p.speedX * delta * p.direction; p.y += p.speedY * delta; if (p.x < 0) p.x = this.width; if (p.x > this.width) p.x = 0; if (p.y < 0) p.y = this.height * 0.6; if (p.y > this.height * 0.6) p.y = 0; } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); // Soleil (rayons tournants) const sunX = this.width - 100; const sunY = 100; const sunRadius = 40; ctx.save(); ctx.translate(sunX, sunY); ctx.rotate(this.sunRotation); ctx.strokeStyle = 'gold'; ctx.lineWidth = 3; for (let i = 0; i < 12; i++) { ctx.beginPath(); ctx.moveTo(0, sunRadius); ctx.lineTo(0, sunRadius + 15); ctx.stroke(); ctx.rotate(Math.PI / 6); } ctx.restore(); ctx.beginPath(); let grad = ctx.createRadialGradient(sunX, sunY, 10, sunX, sunY, sunRadius); grad.addColorStop(0, 'rgba(255, 255, 0, 1)'); grad.addColorStop(1, 'rgba(255, 255, 0, 0)'); ctx.fillStyle = grad; ctx.arc(sunX, sunY, sunRadius, 0, Math.PI * 2); ctx.fill(); // Particules lumineuses ctx.fillStyle = 'rgba(255, 255, 200, 0.5)'; for (let p of this.particles) { ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.fill(); } // Oiseaux (V shape wings) ctx.strokeStyle = 'black'; ctx.lineWidth = 2; for (let b of this.birds) { ctx.beginPath(); ctx.moveTo(b.x, b.y); ctx.lineTo(b.x - b.size, b.y + b.size / 2); ctx.lineTo(b.x, b.y); ctx.lineTo(b.x + b.size, b.y + b.size / 2); ctx.stroke(); } } } // === NUAGEUX === class NuageuxAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.clouds = []; this.createClouds(); } createClouds() { for (let i = 0; i < 10; i++) { this.clouds.push({ x: random(0, this.width), y: random(20, this.height * 0.4), speed: random(10, 40), size: random(60, 120), opacity: random(0.4, 0.8) }); } } update(delta) { for (let c of this.clouds) { c.x += c.speed * delta; if (c.x > this.width + c.size) c.x = -c.size; } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); // Nuages blancs doux ctx.fillStyle = 'rgba(230, 230, 230, 0.7)'; ctx.strokeStyle = 'rgba(200, 200, 200, 0.5)'; ctx.lineWidth = 1; for (let c of this.clouds) { const x = c.x; const y = c.y; const size = c.size; const opacity = c.opacity; ctx.fillStyle = `rgba(230, 230, 230, ${opacity})`; ctx.beginPath(); ctx.ellipse(x, y, size * 0.3, size * 0.2, 0, 0, Math.PI * 2); ctx.ellipse(x + size * 0.3, y + 5, size * 0.35, size * 0.25, 0, 0, Math.PI * 2); ctx.ellipse(x + size * 0.6, y, size * 0.3, size * 0.2, 0, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } } } // === PLUIE === class PluieAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.drops = []; this.nbDrops = 120; this.createDrops(); } createDrops() { this.drops = []; for (let i = 0; i < this.nbDrops; i++) { this.drops.push({ x: random(0, this.width), y: random(0, this.height), length: random(10, 20), speedY: random(150, 300), speedX: random(-20, 20), alpha: random(0.2, 0.6), width: random(1, 2), }); } } update(delta) { for (let d of this.drops) { d.x += d.speedX * delta; d.y += d.speedY * delta; if (d.y > this.height) { d.x = random(0, this.width); d.y = -d.length; } if (d.x < 0) d.x = this.width; if (d.x > this.width) d.x = 0; } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); ctx.strokeStyle = 'rgba(173, 216, 230, 0.6)'; ctx.lineWidth = 1; for (let d of this.drops) { ctx.beginPath(); ctx.moveTo(d.x, d.y); ctx.lineTo(d.x + d.speedX * 0.05, d.y + d.length); ctx.stroke(); } } } // === TEMPETE === class TempeteAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.rain = new PluieAnimation(ctx, width, height); this.lightnings = []; this.lightningTimer = 0; this.lightningDuration = 0.3; this.currentLightning = null; } update(delta) { this.rain.update(delta); this.lightningTimer -= delta; if (this.lightningTimer <= 0) { this.lightningTimer = random(2, 6); this.currentLightning = { x: random(0, this.width), y: random(0, this.height * 0.5), alpha: 1, elapsed: 0, }; } if (this.currentLightning) { this.currentLightning.elapsed += delta; this.currentLightning.alpha = 1 - this.currentLightning.elapsed / this.lightningDuration; if (this.currentLightning.alpha <= 0) { this.currentLightning = null; } } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); this.rain.draw(); if (this.currentLightning) { ctx.save(); ctx.strokeStyle = `rgba(255, 255, 255, ${this.currentLightning.alpha})`; ctx.lineWidth = 3; ctx.beginPath(); // Simple éclair zigzag vertical let x = this.currentLightning.x; let y = this.currentLightning.y; ctx.moveTo(x, y); for (let i = 0; i < 6; i++) { x += (Math.random() - 0.5) * 30; y += this.height * 0.05; ctx.lineTo(x, y); } ctx.stroke(); ctx.restore(); } } } // === NEIGE === class NeigeAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.flakes = []; this.nbFlakes = 100; this.createFlakes(); } createFlakes() { this.flakes = []; for (let i = 0; i < this.nbFlakes; i++) { this.flakes.push({ x: random(0, this.width), y: random(0, this.height), radius: random(1, 3), speedY: random(20, 60), speedX: random(-10, 10), angle: random(0, Math.PI * 2), angleSpeed: random(-0.02, 0.02), }); } } update(delta) { for (let f of this.flakes) { f.x += f.speedX * delta; f.y += f.speedY * delta; f.angle += f.angleSpeed; if (f.y > this.height) { f.x = random(0, this.width); f.y = -f.radius; } if (f.x < 0) f.x = this.width; if (f.x > this.width) f.x = 0; } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'; for (let f of this.flakes) { ctx.save(); ctx.translate(f.x, f.y); ctx.rotate(f.angle); ctx.beginPath(); ctx.moveTo(0, -f.radius); for (let i = 0; i < 6; i++) { ctx.lineTo(0, -f.radius); ctx.rotate(Math.PI / 3); } ctx.fill(); ctx.restore(); } } } // === BRISE === class BriseAnimation extends WeatherAnimation { constructor(ctx, width, height) { super(ctx, width, height); this.particles = []; this.nbParticles = 80; this.createParticles(); } createParticles() { this.particles = []; for (let i = 0; i < this.nbParticles; i++) { this.particles.push({ x: random(0, this.width), y: random(0, this.height), size: random(4, 8), speedX: random(20, 50), alpha: random(0.2, 0.6), }); } } update(delta) { for (let p of this.particles) { p.x += p.speedX * delta; if (p.x > this.width) p.x = -p.size; } } draw() { const ctx = this.ctx; ctx.clearRect(0, 0, this.width, this.height); ctx.strokeStyle = 'rgba(200, 200, 255, 0.5)'; ctx.lineWidth = 2; for (let p of this.particles) { ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(p.x + p.size, p.y); ctx.stroke(); } } } // Dictionnaire météo → classe const animationsMap = { beau: BeauAnimation, nuageux: NuageuxAnimation, pluie: PluieAnimation, tempete: TempeteAnimation, neige: NeigeAnimation, brise: BriseAnimation, }; function startAnimation(weather) { if (currentAnimation) { currentAnimation = null; } const AnimationClass = animationsMap[weather]; if (!AnimationClass) { ctx.clearRect(0, 0, canvas.width, canvas.height); return; } currentAnimation = new AnimationClass(ctx, canvas.width / devicePixelRatio, canvas.height / devicePixelRatio); } // Boucle d’animation function animate(time = 0) { if (!lastTime) lastTime = time; const delta = (time - lastTime) / 1000; lastTime = time; if (currentAnimation) { currentAnimation.update(delta); currentAnimation.draw(); } requestAnimationFrame(animate); } // Fonction de correspondance texte -> type animation function resolveMeteoType(text) { text = text.toLowerCase(); if (text.includes('beau') || text.includes('clair') || text.includes('soleil') || text.includes('dégagé')) { return 'beau'; } if (text.includes('nuageux') || text.includes('nuages') || text.includes('couvert')) { return 'nuageux'; } if (text.includes('pluie') || text.includes('averse') || text.includes('bruine')) { return 'pluie'; } if (text.includes('orage') || text.includes('tempête') || text.includes('tonnerre')) { return 'tempete'; } if (text.includes('neige') || text.includes('neigeux') || text.includes('flocons')) { return 'neige'; } if (text.includes('brise') || text.includes('vent') || text.includes('souffle')) { return 'brise'; } return 'beau'; // valeur par défaut } // Fonction pour construire la clé d’animation météo complète function mapMeteoToAnimation(meteoText, periode) { const meteotype = resolveMeteoType(meteoText); return `${meteotype}_${periode}`; } // Variables globales let periode = 'jour'; // par défaut let animationKey = 'beau_jour'; let currentAnimation = null; let lastTime = 0; // Fonction d'initialisation météo function initWeatherCanvas() { fetch('ajax/get_weather.php') .then(response => { if (!response.ok) throw new Error('Erreur lors de la récupération météo'); return response.json(); }) .then(data => { periode = data.periode || 'jour'; // exemple : "jour" ou "nuit" const meteoText = data.meteo || 'Beau temps'; // exemple : "Beau temps", "Averse", etc. animationKey = mapMeteoToAnimation(meteoText, periode); console.log('Données météo reçues:', data); startAnimation(animationKey); requestAnimationFrame(animate); }) .catch(error => { console.error('Erreur météo :', error); animationKey = 'beau_jour'; startAnimation(animationKey); requestAnimationFrame(animate); }); } function startAnimation(weather) { if (currentAnimation) { currentAnimation = null; } const AnimationClass = animationsMap[weather]; if (!AnimationClass) { ctx.clearRect(0, 0, canvas.width, canvas.height); return; } currentAnimation = new AnimationClass(ctx, canvas.width / devicePixelRatio, canvas.height / devicePixelRatio); lastTime = performance.now(); } // Lancement une fois que tout est prêt window.onload = function () { initWeatherCanvas(); }; }); </script> <?php require_once('../includes/footer.php'); ?>
Merci par avance pour votre aide et vos eclaircissements.
A voir également:
- File_exists
- Canvas gratuit - Télécharger - Divers Photo & Graphisme
- Le clavier de mon telephone ne s'affiche plus - Guide
- Ma clé usb ne s'affiche pas - Guide
- Via michelin carte - Télécharger - Transports & Cartes
- Dp animation maker - Télécharger - Animation