Add a timer

This commit is contained in:
2024-05-06 21:02:39 +04:00
parent 278cd93c17
commit 6443f8fd0e
+325 -197
View File
@@ -15,6 +15,11 @@
const currentFileName = window.location.pathname.split('/').pop() const currentFileName = window.location.pathname.split('/').pop()
const codeCarte = currentFileName.split('.')[0] const codeCarte = currentFileName.split('.')[0]
const startButton = document.getElementById('start')
const restartButton = document.getElementById('restart')
restartButton.setAttribute('disabled', true)
/** /**
* Définir le centre , le niveau de zoom et le style de la carte en fonction du code du département. * Définir le centre , le niveau de zoom et le style de la carte en fonction du code du département.
*/ */
@@ -54,42 +59,6 @@ switch (codeCarte) {
zoomLevel = 9.5 zoomLevel = 9.5
} }
/**
* Initialise et configure la carte
*/
const map = new maplibregl.Map({
container: 'map',
style: `../kat/styles/${styleFileName}`,
center: centerCoordinates,
zoom: zoomLevel
})
if (codeCarte !== '973') {
/**
* Désactive le zoom, le double-clic, le déplacement de la carte pour les cartes autres que celle la Guyane.
*/
map.scrollZoom.disable()
map.doubleClickZoom.disable()
map.dragPan.disable()
} else {
/**
* Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour la Guyane.
*/
map.addControl(new maplibregl.NavigationControl({showCompass: false}))
map.setMinZoom(7)
map.setMaxZoom(9.5)
}
if (codeCarte === 'KAMA') {
/**
* Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour l'Afrique'.
*/
map.addControl(new maplibregl.NavigationControl({showCompass: false}))
map.setMinZoom(2.4)
map.setMaxZoom(9)
map.dragPan.enable()
}
/** /**
* Définit les décalages des popups en fonction de leur position. * Définit les décalages des popups en fonction de leur position.
*/ */
@@ -107,8 +76,16 @@ const popupOffsets = {
/** /**
* Référence aux éléments HTML pour afficher le score. * Référence aux éléments HTML pour afficher le score.
*/ */
const ok = document.getElementById('OK') let ok
const ko = document.getElementById('KO') let ko
/**
* Gestion du temps écoulé
*/
let gameTime = 0
let gameMinute = 0
let gameSecond = 0
let intervalId
/** /**
* État actuel de la commune survolée. * État actuel de la commune survolée.
@@ -125,6 +102,107 @@ let currentZone = null
*/ */
const correctAnswers = new Set() const correctAnswers = new Set()
/**
* Permet de remettre le jeu à 0
*/
function resetGame() {
ok.innerText = 0
ko.innerText = 0
correctAnswers.clear()
clearInterval(intervalId)
gameTime = 0
}
/**
* Active le bouton "Commencer la partie" au chargement de la page
*/
window.onload = function() {
const startElement = document.getElementById('start')
startElement.removeAttribute('disabled')
}
function startGame() {
loadMap()
const question = document.getElementById('question')
const score = document.getElementById('score')
const chrono = document.getElementById('chrono')
question.innerHTML = `<p style='font-weight: bold;'>Trouve ${codeCarte === 'KAMA' ? 'le pays' : 'la commune'}</p><p id='commune' style='font-size: 30px; font-weight: bold; color: #1F51FF'>...</p>`
score.innerHTML = "<p style='color: green; font-weight: bold; margin-right: 2em;'>Correct ✅ : <span style='color: black;' id='OK'>0</span></p><p style='color: red; font-weight: bold;'>Faux ❌ : <span style='color: black;' id='KO'>0</span></p>"
chrono.innerHTML = "<p>Temps écoulé : <span id='time'></span></p>"
const communeElement = document.getElementById('commune')
const timeElement = document.getElementById('time')
communeObserver(communeElement, timeElement)
ok = document.getElementById('OK')
ko = document.getElementById('KO')
startButton.setAttribute('disabled', true)
restartButton.removeAttribute('disabled')
resetGame()
}
/**
* Observe le contenu de l'élément ayant pour id "commune"
*
* @param {object} communeElement
* @param {object} timeElement
*/
function communeObserver(communeElement, timeElement) {
// Options de l'observateur (quelles sont les mutations à observer)
const config = {childList: true, subtree: true, characterData: true}
// Fonction callback à éxécuter quand une mutation est observée
const callback = function (mutationsList) {
for (const mutation of mutationsList) {
if (mutation.type == "childList" || mutation.type == "characterData") {
if (communeElement.textContent.trim() !== "...") {
updateTime(timeElement)
} else {
clearInterval(intervalId)
intervalId = null
}
}
}
}
// Crée une instance de l'observateur liée à la fonction de callback
const observer = new MutationObserver(callback)
// Commence à observer le noeud cible pour les mutations précédemment configurées
observer.observe(communeElement, config)
}
/**
* Mets à jour le contenu de l'élément ayant l'id "time"
*
* @param {object} timeElement
*/
function updateTime(timeElement) {
intervalId = setInterval(() => {
gameTime++
if (gameTime >= 60) {
gameMinute = String(gameTime / 60).split('.')[0]
gameSecond = gameTime % 60
timeElement.innerText = `${gameMinute} minute${gameMinute > 1 ? 's' : ''} ${gameSecond} seconde${gameSecond > 1 ? 's' : ''}`
} else {
timeElement.innerText = `${gameTime} seconde${gameTime > 1 ? 's' : ''}`
}
}, 1000)
}
/**
* Recommencer la partie
*/
function restartGame() {
startGame()
}
/** /**
* Récupère les propriétés des communes depuis un fichier GeoJSON en fonction du code du département. * Récupère les propriétés des communes depuis un fichier GeoJSON en fonction du code du département.
* *
@@ -208,28 +286,6 @@ function pickRandomZone(zones) {
return zonesRestantes[index] return zonesRestantes[index]
} }
/**
* Définit la zone actuelle à trouver et met à jour l'affichage.
*
* @param {Object} zone - La zone à définir comme actuelle.
*/
function setZone(zone) {
if (currentZone) {
map.setFeatureState(
{source: 'communes', id: currentZone.code},
{current: false}
)
}
currentZone = zone
document.getElementById('commune').innerText = zone.nom
map.setFeatureState(
{source: 'communes', id: zone.code},
{current: true}
)
}
/** /**
* Vérifie si le code de la zone cliquée correspond à la commune actuelle à trouver. * Vérifie si le code de la zone cliquée correspond à la commune actuelle à trouver.
* *
@@ -241,165 +297,237 @@ function checkAnswer(codeZone) {
} }
/** /**
* Code pour la gestion des événements de la carte (load, mousemove, mouseleave, click). * Charge la carte
*/ */
map.on('load', async () => { function loadMap() {
const zones = await getFile(codeCarte)
/** /**
* Ajouter une source de données pour les communes. * Initialise et configure la carte
*/ */
map.addSource('communes', { const map = new maplibregl.Map({
'type': 'geojson', container: 'map',
'data': `../kat/data/${codeCarte}/contours-${codeCarte === 'KAMA' ? 'pays' : 'communes'}.geojson`, style: `../kat/styles/${styleFileName}`,
'promoteId': 'code' center: centerCoordinates,
zoom: zoomLevel
}) })
if (codeCarte !== '973') {
/**
* Désactive le zoom, le double-clic, le déplacement de la carte pour les cartes autres que celle la Guyane.
*/
map.scrollZoom.disable()
map.doubleClickZoom.disable()
map.dragPan.disable()
} else {
/**
* Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour la Guyane.
*/
map.addControl(new maplibregl.NavigationControl({showCompass: false}))
map.setMinZoom(7)
map.setMaxZoom(9.5)
}
if (codeCarte === 'KAMA') {
/**
* Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour l'Afrique'.
*/
map.addControl(new maplibregl.NavigationControl({showCompass: false}))
map.setMinZoom(2.4)
map.setMaxZoom(9)
map.dragPan.enable()
}
/** /**
* Ajouter une couche de remplissage pour les communes. * Code pour la gestion des événements de la carte (load, mousemove, mouseleave, click).
*/ */
map.addLayer({ map.on('load', async () => {
'id': 'drom', const zones = await getFile(codeCarte)
'type': 'fill',
'source': 'communes', /**
'layout': {}, * Définit la zone actuelle à trouver et met à jour l'affichage.
'paint': { *
'fill-color': '#1F51FF', * @param {Object} zone - La zone à définir comme actuelle.
'fill-opacity': [ */
'case', function setZone(zone) {
['boolean', ['feature-state', 'hover'], false], if (currentZone) {
0.8, map.setFeatureState(
0 {source: 'communes', id: currentZone.code},
] {current: false}
)
}
currentZone = zone
document.getElementById('commune').innerText = zone.nom
map.setFeatureState(
{source: 'communes', id: zone.code},
{current: true}
)
} }
})
/** /**
* Ajouter une couche de remplissage pour les communes (2ème couche), en prenant en compte les réponses correctes et incorrectes. * Ajouter une source de données pour les communes.
*/ */
map.addLayer({ map.addSource('communes', {
'id': 'drom-2', 'type': 'geojson',
'type': 'fill', 'data': `../kat/data/${codeCarte}/contours-${codeCarte === 'KAMA' ? 'pays' : 'communes'}.geojson`,
'source': 'communes', 'promoteId': 'code'
'layout': {}, })
'paint': {
'fill-color': [
'case',
['boolean', ['feature-state', 'correct'], false], '#00FF00',
['boolean', ['feature-state', 'incorrect'], false], '#FF0000',
'rgba(0,0,0,0)'
],
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false], 0.5,
0.8
]
}
})
/** /**
* Gère l'evenement mousemove (quand la souris survole la commune) * Ajouter une couche de remplissage pour les communes.
*/ */
map.on('mousemove', 'drom', e => { map.addLayer({
map.getCanvas().style.cursor = 'pointer' 'id': 'drom',
if (e.features.length > 0) { 'type': 'fill',
'source': 'communes',
'layout': {},
'paint': {
'fill-color': '#1F51FF',
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
0.8,
0
]
}
})
/**
* Ajouter une couche de remplissage pour les communes (2ème couche), en prenant en compte les réponses correctes et incorrectes.
*/
map.addLayer({
'id': 'drom-2',
'type': 'fill',
'source': 'communes',
'layout': {},
'paint': {
'fill-color': [
'case',
['boolean', ['feature-state', 'correct'], false], '#00FF00',
['boolean', ['feature-state', 'incorrect'], false], '#FF0000',
'rgba(0,0,0,0)'
],
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false], 0.5,
0.8
]
}
})
/**
* Gère l'evenement mousemove (quand la souris survole la commune)
*/
map.on('mousemove', 'drom', e => {
map.getCanvas().style.cursor = 'pointer'
if (e.features.length > 0) {
if (hoveredStateId) {
map.setFeatureState(
{source: 'communes', id: hoveredStateId},
{hover: false}
)
}
hoveredStateId = e.features[0].id
map.setFeatureState(
{source: 'communes', id: hoveredStateId},
{hover: true}
)
}
})
/**
* Gère l'evenement mouseleave (quand la souris quitte la zone)
*/
map.on('mouseleave', 'drom', () => {
map.getCanvas().style.cursor = ''
if (hoveredStateId) { if (hoveredStateId) {
map.setFeatureState( map.setFeatureState(
{source: 'communes', id: hoveredStateId}, {source: 'communes', id: hoveredStateId},
{hover: false} {hover: false}
) )
} }
hoveredStateId = e.features[0].id hoveredStateId = null
map.setFeatureState( })
{source: 'communes', id: hoveredStateId},
{hover: true}
)
}
})
/** /**
* Gère l'evenement mouseleave (quand la souris quitte la zone) * Gère l'evenement click (quand on clique sur la zone)
*/ */
map.on('mouseleave', 'drom', () => { map.on('click', 'drom', async e => {
map.getCanvas().style.cursor = '' if (e.features.length > 0) {
if (hoveredStateId) { const clickedCodeZone = e.features[0].id
map.setFeatureState( const clickedZoneNom = e.features[0].properties.nom
{source: 'communes', id: hoveredStateId}, const isCorrect = checkAnswer(clickedCodeZone)
{hover: false}
)
}
hoveredStateId = null
})
/** if (isCorrect) {
* Gère l'evenement click (quand on clique sur la zone) updateScore(true)
*/ const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone)
map.on('click', 'drom', async e => {
if (e.features.length > 0) {
const clickedCodeZone = e.features[0].id
const clickedZoneNom = e.features[0].properties.nom
const isCorrect = checkAnswer(clickedCodeZone)
if (isCorrect) { const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ok'})
updateScore(true) .setLngLat([centerCoordinates[0], centerCoordinates[1]])
const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone) .setHTML(`<h3>Bravo, c'est ${clickedZoneNom}</h3>`)
.setMaxWidth('200px')
.addTo(map)
const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ok'})
.setLngLat([centerCoordinates[0], centerCoordinates[1]])
.setHTML(`<h3>Bravo, c'est ${clickedZoneNom}</h3>`)
.setMaxWidth('200px')
.addTo(map)
setTimeout(() => {
popup.remove()
}, 1500)
correctAnswers.add(clickedCodeZone)
zones.forEach(commune => {
map.setFeatureState(
{source: 'communes', id: commune.code},
{incorrect: false}
)
})
map.setFeatureState(
{source: 'communes', id: clickedCodeZone},
{correct: true, current: false}
)
const newZone = pickRandomZone(zones)
if (newZone) {
setZone(newZone)
} else {
const totalAnswers = Number.parseInt(ok.innerText, 10) + Number.parseInt(ko.innerText, 10)
const percentageCorrectAnswers = (Number.parseInt(ok.innerText, 10) / totalAnswers) * 100
alert(`Fin du jeu, ${Math.round(percentageCorrectAnswers)} % de bonnes réponses !`, )
setTimeout(() => { setTimeout(() => {
location.reload() popup.remove()
}, 1500) }, 1500)
correctAnswers.add(clickedCodeZone)
zones.forEach(commune => {
map.setFeatureState(
{source: 'communes', id: commune.code},
{incorrect: false}
)
})
map.setFeatureState(
{source: 'communes', id: clickedCodeZone},
{correct: true, current: false}
)
const newZone = pickRandomZone(zones)
if (newZone) {
setZone(newZone)
} else {
const totalAnswers = Number.parseInt(ok.innerText, 10) + Number.parseInt(ko.innerText, 10)
const percentageCorrectAnswers = (Number.parseInt(ok.innerText, 10) / totalAnswers) * 100
let elapsedTime = `${gameTime} seconde${gameTime > 1 ? 's' : ''}`
if (gameTime >= 60) {
elapsedTime = `${gameMinute} minute${gameMinute > 1 ? 's' : ''} ${gameSecond} seconde${gameSecond > 1 ? 's' : ''}`
}
alert(`Fin du jeu, ${Math.round(percentageCorrectAnswers)} % de bonnes réponses ! Temps écoulé : ${elapsedTime}`)
clearInterval(intervalId)
gameTime = 0
setTimeout(() => {
location.reload()
}, 1500)
}
} else {
updateScore(false)
const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone)
const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ko'})
.setLngLat([centerCoordinates[0], centerCoordinates[1]])
.setHTML(`<h3>Faux, c'est ${clickedZoneNom}</h3>`)
.setMaxWidth('200px')
.addTo(map)
setTimeout(() => {
popup.remove()
}, 1500)
map.setFeatureState(
{source: 'communes', id: clickedCodeZone},
{incorrect: true}
)
} }
} else {
updateScore(false)
const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone)
const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ko'})
.setLngLat([centerCoordinates[0], centerCoordinates[1]])
.setHTML(`<h3>Faux, c'est ${clickedZoneNom}</h3>`)
.setMaxWidth('200px')
.addTo(map)
setTimeout(() => {
popup.remove()
}, 1500)
map.setFeatureState(
{source: 'communes', id: clickedCodeZone},
{incorrect: true}
)
} }
} })
})
const zone = pickRandomZone(zones) const zone = pickRandomZone(zones)
setZone(zone) setZone(zone)
}) })
}