Merge branch 'master' into 'production'

Master

See merge request Romulus21/portal!16
This commit is contained in:
Romain Delanoë
2020-04-04 16:57:37 +00:00
10 changed files with 323 additions and 263 deletions

View File

@@ -9,7 +9,7 @@ import MemoCreate from "./views/Memo/MemoCreate";
import MemoShow from "./views/Memo/MemoShow";
import MemoEdit from "./views/Memo/MemoEdit";
import GameIndex from "./views/Games/GameIndex";
import Hangman from "./views/Games/Hangman";
import Hangman from "./views/Games/HangMan/Hangman";
Vue.use(VueRouter)

View File

@@ -1,121 +1,19 @@
<template>
<div class="p-2">
<h1>Liste des jeux</h1>
<div>
<router-link to="/jeux/pendu">
Pendu
</router-link>
<div class="game-index">
<HangmanIndex link="/jeux/pendu" />
</div>
</div>
</template>
<script>
import HangmanIndex from "./HangMan/HangmanIndex";
export default {
name: "GameIndex"
name: "GameIndex",
components: {
HangmanIndex
}
}
</script>
<style scoped>
.figure-container {
fill: transparent;
stroke: #fff;
stroke-width: 4px;
stroke-linecap: round;
}
.figure-part {
display: none;
}
.wrong-letters-container {
position: absolute;
top: 20px;
right: 20px;
display: flex;
flex-direction: column;
text-align: right;
}
.wrong-letters-container p {
margin: 0 0 5px;
}
.wrong-letters-container span {
font-size: 24px;
}
.word {
display: flex;
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
.word .letter {
border-bottom: 3px solid #2980b9;
display: inline-flex;
font-size: 30px;
align-items: center;
justify-content: center;
margin: 0 3px;
height: 50px;
width: 20px;
}
.popup-container {
background-color: rgba(0, 0, 0, 0.3);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
display: flex;
display: none;
align-items: center;
justify-content: center;
}
.popup-container .popup {
background-color: #2980b9;
border-radius: 5px;
box-shadow: 0 15px 10px 3px rgba(0, 0, 0, 0.1);
padding: 20px;
text-align: center;
}
button {
cursor: pointer;
background-color: #fff;
color: #2980b9;
border: 0;
margin-top: 20px;
padding: 12px 20px;
font-size: 16px;
}
button:active {
transform: scale(0.98);
}
button:focus {
outline: 0;
}
.notification-container {
background-color: rgba(0, 0, 0, 0.3);
border-radius: 10px 10px 0 0;
padding: 15px 20px;
position: absolute;
bottom: -50px;
transition: transform 0.3s ease-in-out;
}
.notification-container p {
margin: 0;
}
.notification-container .show {
transform: translateY(-50px);
}
</style>

View File

@@ -0,0 +1,157 @@
<template>
<div class="p-2 game-container">
<h1 class="text-center">Pendu</h1>
<p class="text-center">Trouve le mot du pendu - Saisi les lettres</p>
<div class="flex">
<div class="game-container-box">
<svg height="250" width="200" class="figure-container">
<!-- Potence -->
<line x1="60" y1="20" x2="140" y2="20" />
<line x1="140" y1="20" x2="140" y2="50" />
<line x1="60" y1="20" x2="60" y2="230" />
<line x1="20" y1="230" x2="100" y2="230" />
<!-- Tête -->
<circle cx="140" cy="70" r="20" class="figure-part"/>
<!-- Corps -->
<line x1="140" y1="90" x2="140" y2="150" class="figure-part"/>
<line x1="140" y1="120" x2="120" y2="100" class="figure-part"/>
<line x1="140" y1="120" x2="160" y2="100" class="figure-part"/>
<line x1="140" y1="150" x2="120" y2="180" class="figure-part"/>
<line x1="140" y1="150" x2="160" y2="180" class="figure-part"/>
</svg>
</div>
<div class="game-container-box">
<div class="wrong-letters-container">
<div v-html="wrongLettersEl" class="wrong-letters"></div>
<div class="word" v-html="wordEl"></div>
</div>
<div v-if="notification" class="notification-container" id="notification-container">
<p>Vous avez déjà saisie cette lettre.</p>
</div>
<div v-if="popup" class="popup-container" id="popup-container">
<div class="popup">
<h2>{{ finalMessage }}</h2>
<button id="play-button" @click="playAgain">Rejouer</button>
</div>
</div>
</div>
</div>
<div class="game-input">
<label for="saisie" class="pb-1">Saisir vos lettres ici</label>
<input v-model="letterBox" id="saisie" type="text" @keyup="keyMessage">
</div>
</div>
</template>
<script>
export default {
name: "Hangman",
data: function () {
return {
words: ['pimprenelle', 'culcul', 'milo', 'Prout'],
letterBox: '',
figureParts: [],
correctLetters: [],
wrongLetters: [],
wrongLettersEl: '',
selectedWord: '',
wordEl: '',
finalMessage: '',
popup: false,
notification: false,
}
},
mounted() {
this.selectedWord = this.words[Math.floor(Math.random() * this.words.length)].toLowerCase()
this.figureParts = this.$el.querySelectorAll('.figure-part')
this.displayWord()
},
methods: {
keyMessage(e) {
console.log(event.key, event.keyCode)
if(e.keyCode >= 65 && e.keyCode <= 90) {
const letter = e.key
if(this.selectedWord.includes(letter)) {
if(!this.correctLetters.includes(letter)) {
this.correctLetters.push(letter)
this.displayWord()
} else {
this.showNotification()
}
} else {
if(!this.wrongLetters.includes(letter)) {
this.wrongLetters.push(letter);
this.updateWrongLettersEl()
} else {
this.showNotification()
}
}
}
},
displayWord() {
this.wordEl =
this.selectedWord
.split('')
.map(
letter => {
let toggleLetter = this.correctLetters.includes(letter) ? letter : '';
return '<span class="letter">' + toggleLetter +"</span>"
}
).join('');
const innerWord = this.wordEl.replace(/<span class="letter">|<\/span>/g, '');
console.log('win', innerWord, this.selectedWord)
if(innerWord === this.selectedWord) {
this.finalMessage = 'Gagné! 😃';
this.popup = true
}
},
updateWrongLettersEl() {
console.log('wrong', this.wrongLettersEl)
this.wrongLettersEl =
!this.wrongLetters ? '<p>Wrong</p>' : ''
+
this.wrongLetters.map(letter => `<span>${letter}</span>`);
// Display parts
this.figureParts.forEach((part, index) => {
const errors = this.wrongLetters.length;
if(index < errors) {
part.style.display = 'block'
} else {
part.style.display = 'none'
}
});
// Check if lost
if(this.wrongLetters.length === this.figureParts.length) {
this.finalMessage = 'Perdu. 😕';
this.popup = true
}
},
playAgain: function () {
this.correctLetters.splice(0);
this.wrongLetters.splice(0);
this.selectedWord = this.words[Math.floor(Math.random() * this.words.length)].toLowerCase();
this.displayWord();
this.updateWrongLettersEl();
this.popup = false
this.letterBox = ''
},
showNotification(){
this.notification = true
setTimeout(() => {
this.notification = false
}, 2000)
}
}
}
</script>

View File

@@ -0,0 +1,13 @@
<template>
<router-link :to="link" class="game-index-item">
<svg-vue icon="hangman" />
<h2>Pendu</h2>
</router-link>
</template>
<script>
export default {
name: "HangmanIndex",
props: ["link"],
}
</script>

View File

@@ -1,115 +0,0 @@
<template>
<div class="p-2">
<h1>Pendu</h1>
<p>Trouve le mot du pendu - Saisi les lettres</p>
<div class="game-container">
<svg height="250" width="200" class="figure-container">
<!-- Potence -->
<line x1="60" y1="20" x2="140" y2="20" />
<line x1="140" y1="20" x2="140" y2="50" />
<line x1="60" y1="20" x2="60" y2="230" />
<line x1="20" y1="230" x2="100" y2="230" />
<!-- Tête -->
<circle cx="140" cy="70" r="20" class="figure-part"/>
<!-- Corps -->
<line x1="140" y1="90" x2="140" y2="150" class="figure-part"/>
<line x1="140" y1="120" x2="120" y2="100" class="figure-part"/>
<line x1="140" y1="120" x2="160" y2="100" class="figure-part"/>
<line x1="140" y1="150" x2="120" y2="180" class="figure-part"/>
<line x1="140" y1="150" x2="160" y2="180" class="figure-part"/>
</svg>
<div class="wrong-letters-container">
<div v-html="wrongLetters"></div>
</div>
<div class="word" v-html="wordEl"></div>
</div>
<div class="popup-container" id="popup-container">
<div class="popup">
<h2 id="final-message"></h2>
<button id="play-button" @click="playAgain">Play Again</button>
</div>
</div>
<div class="notification-container" id="notification-container">
<p>You have already entered this letter</p>
</div>
</div>
</template>
<script>
export default {
name: "Hangman",
data: function () {
return {
words: ['pimprenelle', 'culcul', 'milo'],
correctLetters: [],
wrongLetters: [],
selectedWord: '',
wordEl: '',
popup: false,
finalMessage: '',
}
},
mounted() {
this.selectedWord = this.words[Math.floor(Math.random() * this.words.length)]
console.log(this.selectedWord)
this.displayWord()
},
methods: {
displayWord() {
this.wordEl =
this.selectedWord
.split('')
.map(
letter => {
let toggleLetter = this.correctLetters.includes(letter) ? letter : ''
return '<span class="' + letter + '">' + toggleLetter +"</span>"
}
).join('')
const innerWord = this.wordEl.replace(/\n/g, '')
if(innerWord === this.selectedWord) {
this.finalMessage = 'Congratulations! You won! 😃';
this.popup.style.display = 'flex';
}
},
updateWrongLettersEl() {
let wrongLettersEl =
this.wrongLetters.length > 0 ? '<p>Wrong</p>' : ''
+
this.wrongLetters.map(letter => `<span>${letter}</span>`)
// Display parts
figureParts.forEach((part, index) => {
const errors = wrongLetters.length
if(index < errors) {
part.style.display = 'block'
} else {
part.style.display = 'none'
}
})
// Check if lost
if(wrongLetters.length === figureParts.length) {
finalMessage.innerText = 'Unfortunately you lost. 😕';
popup.style.display = 'flex';
}
},
playAgain: function () {
this.correctLetters.splice(0)
this.wrongLetters.splice(0)
this.selectedWord = this.words[Math.floor(Math.random() * this.words.length)]
this.displayWord()
this.updateWrongLettersEl()
this.popup = false
}
}
}
</script>

View File

@@ -18,5 +18,6 @@
@import "pages/auth";
@import "pages/memos";
@import "pages/meteo";
@import "pages/games";
@import "pages/games/hangman";

20
resources/sass/pages/games.scss vendored Normal file
View File

@@ -0,0 +1,20 @@
.game-index {
display: flex;
flex-wrap: wrap;
margin-top: 3rem;
}
.game-index-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 18rem;
height: 18rem;
background-color: $light;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}

View File

@@ -1,48 +1,55 @@
.game-container {
padding: 20px 30px;
max-width: 80rem;
margin: 0 auto;
}
.game-container-box {
padding: 2rem 3rem;
position: relative;
margin: auto;
height: 350px;
width: 450px;
display: flex;
flex: 1;
flex-direction: column;
justify-content: space-between;
}
.figure-container {
fill: transparent;
stroke: $dark;
stroke: $mediumDark;
stroke-width: 4px;
stroke-linecap: round;
}
.figure-part {
display: none;
stroke: $dark;
}
.wrong-letters-container {
position: absolute;
top: 20px;
right: 20px;
top: 4rem;
display: flex;
flex-direction: column;
text-align: right;
.wrong-letters {
height: 3.5rem;
}
p {
margin: 0 0 5px;
}
span {
font-size: 24px;
font-size: 2.4rem;
}
}
.word {
display: flex;
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
align-self: center;
justify-self: center;
bottom: 1rem;
.letter {
border-bottom: 3px solid #2980b9;
border-bottom: 3px solid $medium;
display: inline-flex;
font-size: 30px;
align-items: center;
@@ -53,29 +60,27 @@
}
}
.game-input {
max-width: 30rem;
margin: 0 auto;
text-align: center;
}
.popup-container {
background-color: rgba(0,0,0,0.3);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
display: flex;
display: none;
align-items: center;
justify-content: center;
position: absolute;
width: calc(100% - 6rem);
.popup {
background-color: #2980b9;
background-color: $medium;
border-radius: 5px;
box-shadow: 0 15px 10px 3px rgba(0,0,0,0.1);
color: $light;
padding: 20px;
text-align: center;
button {
cursor: pointer;
background-color: #fff;
color: #2980b9;
color: $dark;
border: 0;
margin-top: 20px;
padding: 12px 20px;
@@ -93,18 +98,13 @@
}
.notification-container {
background-color: rgba(0,0,0,0.3);
border-radius: 10px 10px 0 0;
padding: 15px 20px;
position: absolute;
bottom: -50px;
transition: transform 0.3s ease-in-out;
background-color: $mediumLight;
border-radius: 2px;
padding: 1rem;
text-align: center;
margin-bottom: 6rem;
p {
margin: 0;
}
&.show {
transform: translateY(-50px);
}
}

View File

@@ -1,6 +1,11 @@
.meteo {
width: 25rem;
&-date {
font-size: 1.2rem;
}
.owf {
color: $mediumDark;
}
}

81
resources/svg/hangman.svg Normal file
View File

@@ -0,0 +1,81 @@
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
height="250"
width="200"
class="figure-container">
<style
id="style2">stroke: 5px;</style>
<!-- Potence -->
<line
x1="60"
y1="20"
x2="140"
y2="20"
id="line4"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round" />
<line
x1="140"
y1="20"
x2="140"
y2="50"
id="line6"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none" />
<line
x1="60"
y1="20"
x2="60"
y2="230"
id="line8"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none" />
<line
x1="20"
y1="230"
x2="100"
y2="230"
id="line10"
style="stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke:#000000;stroke-opacity:1" />
<!-- Tête -->
<circle
cx="140"
cy="70"
r="20"
id="circle12"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;fill:none" />
<!-- Corps -->
<line
x1="140"
y1="90"
x2="140"
y2="150"
id="line14"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none" />
<line
x1="140"
y1="120"
x2="120"
y2="100"
id="line16"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round" />
<line
x1="140"
y1="120"
x2="160"
y2="100"
id="line18"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round" />
<line
x1="140"
y1="150"
x2="120"
y2="180"
id="line20"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round" />
<line
x1="140"
y1="150"
x2="160"
y2="180"
id="line22"
style="stroke:#000000;stroke-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB