Skip to content

Instantly share code, notes, and snippets.

@fagnerlaselva
Last active November 27, 2018 15:33
Show Gist options
  • Save fagnerlaselva/5b6986806b955ce88254496af21aa43d to your computer and use it in GitHub Desktop.
Save fagnerlaselva/5b6986806b955ce88254496af21aa43d to your computer and use it in GitHub Desktop.
Who's that Pokémon?
<div id="app">
<div
:class="{'poke-classic': classic}"
class="container"
>
<transition name="animate-section">
<section
v-if="!isPlaying && !isDone"
class="poke-section"
>
<h2>What type of trainer are you?</h2>
<div class="poke-intro-trainer">
<div class="poke-ball"></div>
<img
:class="{active: trainerHovered === 'classic'}"
class="poke-trainer-img poke-trainer-img-classic"
src="https://raw.githubusercontent.com/tiffachoo/pokesprites/master/trainers/red-rb.png"
alt="Trainer red"
>
<img
:class="{active: trainerHovered === 'master'}"
class="poke-trainer-img poke-trainer-img-master"
src="https://raw.githubusercontent.com/tiffachoo/pokesprites/master/trainers/red-sm.png"
alt="Trainer red again"
>
</div>
<button
class="button spacer"
@click="startGame(151)"
@mouseover="trainerHover('classic')"
@mouseout="trainerHover"
>
Classic
</button>
<button
class="button"
@click="startGame(0)"
@mouseover="trainerHover('master')"
@mouseout="trainerHover"
>
Master
</button>
</section>
</transition>
<transition name="animate-section">
<section
v-if="isPlaying"
class="poke-section"
>
<h1 class="poke-title">
Who's that pokemon?
</h1>
<div class="poke-question-wrapper">
<span class="poke-question">
<span class="poke-question-number">
{{ question }}
</span>
<span class="poke-question-amount">
/ {{ questionAmount}}
</span>
</span>
<span class="poke-score">
{{ score }}
<small>pts</small>
</span>
<div
class="poke-image"
:class="{'poke-image-success': isChecked && selected.name === answer.name, 'poke-image-error': isChecked && selected.name !== answer.name}"
>
<img
:src="image"
alt="No cheating"
class="poke-image-img"
>
</div>
<transition-group
tag="div"
name="animate-options"
:class="{'poke-options-answers': isChecked}"
class="poke-options"
>
<button
v-for="(pokemon, index) in options"
:key="pokemon.name"
:data-index="index"
:class="{'selected': selected.index === index, 'success': isChecked && pokemon.name === answer.name , 'error': isChecked && selected.index === index && selected.name !== answer.name}"
class="poke-options-button"
@click="selectAnswer(pokemon.name, index)"
>{{ pokemon.name | prettifyName }}</button>
</transition-group>
<footer class="poke-buttons">
<button
:disabled="isChecked || Object.keys(selected).length < 1"
class="button"
@click="checkAnswer"
>Submit</button>
<button
:disabled="!isChecked"
class="button"
@click="getNextQuestion"
>Next</button>
</footer>
</div>
</section>
</transition>
<transition name="animate-section">
<section
v-if="isDone"
class="poke-final"
>
<h2>Final score</h2>
<span class="poke-final-score">
<span class="poke-final-score-number">{{ score }}</span>
pts
</span>
<button
class="button"
@click="restartGame"
>Play again</button>
</section>
</transition>
</div>
</div>
console.clear();
const pkmnTotal = 802;
const url = `https://pokeapi.co/api/v2/pokemon/?limit=${pkmnTotal}`;
const optionAmount = 4;
let pokemonData = [];
const prettyNames = {
'nidoran-f': 'nidoran♀',
'nidoran-m': 'nidoran♂',
'mr-mime': 'mr. mime',
'deoxys-normal': 'deoxys',
'wormadam-plant': 'wormadam',
'mime-jr': 'mime jr.',
'giratina-altered': 'giratina',
'shaymin-land': 'shaymin',
'basculin-red-striped': 'basculin',
'darmanitan-standard': 'darmanitan',
'tornadus-incarnate': 'tornadus',
'thundurus-incarnate': 'thundurus',
'landorus-incarnate': 'landorus',
'keldeo-ordinary': 'keldeo',
'meloetta-aria': 'meloetta',
'meowstic-male': 'meowstic',
'aegislash-shield': 'aegislash',
'pumpkaboo-average': 'pumpkaboo',
'gourgeist-average': 'gourgeist',
'oricorio-baile': 'oricorio',
'lycanroc-midday': 'lycanroc',
'wishiwashi-solo': 'wishiwashi',
'type-null': 'type: null',
'minior-red-meteor': 'minior',
'mimikyu-disguised': 'mimikyu',
'tapu-koko': 'tapu koko',
'tapu-lele': 'tapu lele',
'tapu-bulu': 'tapu bulu',
'tapu-fini': 'tapu fini'
}
const app = new Vue({
el: '#app',
filters: {
prettifyName(name) {
return prettyNames[name] || name;
}
},
data() {
return {
pokemon: [],
pkmnAmount: null,
score: 0,
question: 0,
questionAmount: 10,
answer: {},
selected: {},
options: [],
isPlaying: false,
isDone: false,
isChecked: false,
trainerHovered: null
}
},
computed: {
image() {
let url = 'https://raw.githubusercontent.com/tiffachoo/pokesprites/master/pokemon/'
let imageUrl = `${url}${this.classic ? 'redblue' : 'sunmoon'}/`
let number = this.answer.url.match(/\/(\d+)/)[1];
return `${imageUrl}${number}.png`
},
classic() {
return this.pkmnAmount <= 151
}
},
mounted() {
let pokeList = localStorage.getItem('pokeList');
if (pokeList) {
pokemonData = JSON.parse(pokeList);
} else {
this.getData()
.then(res => {
pokemonData = res.results
localStorage.setItem('pokeList', JSON.stringify(res.results));
});
}
},
methods: {
getData() {
return fetch(url)
.then(res => res.json())
.catch(err => console.log('errrr'));
},
startGame(val) {
this.question = 0;
this.score = 0;
this.isPlaying = true;
this.pokemon = [...pokemonData];
this.pkmnAmount = val || pkmnTotal;
this.getNextQuestion();
},
getNextQuestion() {
this.question += 1;
this.resetAnswer();
if (this.question <= this.questionAmount) {
let removed = '';
for (let i = 1; i <= optionAmount; i++) {
removed = this.pokemon.splice(this.getRandomPokemon(i), 1)[0];
if (i === 1) {
this.answer = removed;
} else {
this.options.push(removed);
}
}
let pos = Math.floor(Math.random() * optionAmount);
this.options.splice(pos, 0, this.answer);
} else {
this.isPlaying = false;
this.isDone = true;
this.resetAnswer();
}
},
selectAnswer(ans, index) {
if (!this.isChecked) {
this.$set(this.selected, 'name', ans);
this.$set(this.selected, 'index', index);
}
},
checkAnswer() {
this.isChecked = true;
if (this.selected.name === this.answer.name) {
this.score += 10;
}
},
getRandomPokemon(index) {
const diff = (this.question - 1) * 4 + index;
return Math.floor(Math.random() * (this.pkmnAmount + 1 - diff));
},
resetAnswer() {
this.options = [];
this.selected = {};
this.answer = {};
this.isChecked = false;
},
restartGame() {
this.isDone = false;
},
trainerHover(val) {
this.trainerHovered = val;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
@import 'https://fonts.googleapis.com/css?family=VT323';
$black: #333;
$white: #fff;
$primary-color: #f65a52;
$primary-color-dark: darken( $primary-color, 10% );
$primary-color-tint: lighten( $primary-color, 15% );
$primary-font: "VT323", monospace;
$secondary-color: #94acbd;
$secondary-color-dark: darken( $secondary-color, 10% );
$options-color: #c5d5ee;
$options-color-hover: darken( $options-color, 7% );
$success-color: #7bd55a;
$error-color: #ff8b62;
$border-radius-main: 1rem;
$border-width: 6px;
$border-main: solid $border-width $black;
$options-height: 48px;
* { box-sizing: border-box; }
body {
background-color: $primary-color;
font-family: $primary-font;
font-size: 16px;
line-height: 1.875em;
color: $black;
}
.container {
width: 100%;
max-width: 400px;
position: relative;
margin: 50px auto;
}
h2 {
font-size: 1.25rem;
white-space: nowrap;
}
.spacer {
margin-bottom: 0.5rem;
}
.button {
padding: 0.5em 1.5em;
border-radius: $border-radius-main;
border: solid 1px transparent;
font-family: $primary-font;
font-size: 1.5rem;
background-color: $primary-color-dark;
color: $black;
cursor: pointer;
transition: 0.35s;
&:focus {
outline: none;
border: 1px dotted lighten($primary-color, 8%);
}
&:not([disabled]) {
&:hover {
background-color: $black;
color: $primary-color;
}
}
}
.poke-section {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
max-width: 500px;
margin: auto;
}
.poke-intro-trainer {
position: relative;
margin-bottom: 1rem;
height: 200px;
width: 200px;
.poke-trainer-img {
position: absolute;
left: 50%;
bottom: 0;
height: 200px;
opacity: 0;
transition: 0.4s cubic-bezier(.22,.75,.53,.99);
@media (max-width: 479px) {
display: none;
}
&.active {
transform: translateX(-50%);
opacity: 1;
}
&-classic {
bottom: 5px;
height: 180px;
image-rendering: pixelated;
transform: translateX(-80%);
}
&-master {
transform: translateX(-20%);
}
}
}
.poke-ball {
position: absolute;
top: 50%;
left: 50%;
height: 150px;
width: 150px;
border-radius: 50%;
background-color: $primary-color-dark;
transform: translate(-50%, -50%);
overflow: hidden;
&::before,
&::after {
content: '';
position: absolute;
}
&::before {
z-index: 2;
top: 50%;
left: 50%;
height: 40px;
width: 40px;
border-radius: 50%;
border: solid $border-width $primary-color;
background-color: $primary-color-tint;
transform: translate(-50%, -50%);
}
&::after {
z-index: 1;
top: 50%;
height: 50%;
width: 100%;
border-top: solid $border-width $primary-color;
background-color: $primary-color-tint;
}
}
.poke-title {
position: absolute;
top: -2rem;
}
.poke-question {
position: absolute;
right: calc(100% + 0.5rem);
display: flex;
flex-direction: column;
align-items: flex-end;
&-wrapper {
position: relative;
width: 250px;
}
&-number {
font-size: 8rem;
line-height: 0.4;
color: $primary-color-tint;
}
}
.poke-score {
position: absolute;
top: 6rem;
right: calc(100% + 0.5rem);
padding-top: 1rem;
font-size: 1.25rem;
white-space: nowrap;
color: $black;
&::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 40px;
height: $border-width;
background-color: $black;
}
}
.poke-image {
position: relative;
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
width: 250px;
height: 250px;
border-radius: $border-radius-main;
border: $border-main;
background-color: $white;
overflow: hidden;
&::before,
&::after {
content: '';
position: absolute;
z-index: -1;
border-radius: 50%;
}
&::before {
width: 100px;
height: 100px;
background-color: $options-color;
opacity: 1;
transition: 0.65s ease-in-out;
}
&::after {
width: 100px;
height: 100px;
border: solid ($border-width * 2) $options-color;
transform: scale(0);
transition: 0.4s ease-in-out;
}
&-img {
width: auto;
height: 150px;
}
&-success,
&-error {
&::before {
transform: scale(4);
opacity: 0.5;
}
&::after {
transform: scale(1);
}
}
&-success {
&::before {
background-color: $success-color;
}
&::after {
border-color: $success-color;
}
}
&-error {
&::before {
background-color: $error-color;
}
&::after {
border-color: $error-color;
width: 10px;
border-radius: $border-radius-main;
transform: rotate(45deg);
}
}
}
.poke-options {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
z-index: 3;
top: -30px;
padding: 0 20px;
margin: 0 auto;
width: 170px;
border-radius: $border-radius-main;
background-color: $black;
&:not(.poke-options-answers) {
.poke-options-button {
&:not(.selected) {
&:hover {
background-color: $options-color-hover;
// color: $primary-color-dark;
transform: translateY(-$border-width / 2);
}
&:active {
&::before {
transform: translate(-50%, -50%) scale(1);
}
}
}
}
}
&.poke-options-answers {
.poke-options-button {
cursor: default;
&:not(.error):not(.success) {
color: $secondary-color;
}
}
}
&-button {
position: relative;
width: 100%;
padding: 0.5em;
min-width: 200px;
max-height: $options-height;
border: $border-main;
border-radius: $border-radius-main;
background-color: $options-color;
font-family: $primary-font;
font-size: 1.125rem;
transition: 0.45s;
cursor: pointer;
overflow: hidden;
&:focus {
outline: none;
}
&::before {
content: '';
position: absolute;
z-index: -1;
left: 50%;
top: 50%;
height: 200px;
width: 200px;
border-radius: 50%;
background-color: $secondary-color;
transform: translate(-50%, -50%) scale(0);
transition: 0.2s ease-in-out;
}
&:not(:last-child) {
margin-bottom: $border-width / 2;
}
&.selected {
background-color: $secondary-color;
}
&.error {
background-color: $error-color;
}
&.success {
background-color: $success-color;
}
}
}
.poke-buttons {
text-align: center;
@media (min-width: 480px) {
position: absolute;
top: 20px;
left: 100%;
.button {
padding-left: calc(1em + 10px);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
transform: translateX(-10px);
}
}
.button {
padding: 1em;
width: 110px;
height: 100px;
color: $white;
&[disabled] {
color: $primary-color-tint;
opacity: 0.7;
cursor: default;
}
&:not([disabled]) {
&:hover {
transform: translateX(0);
}
}
&:not(:last-child) {
margin-bottom: $border-width;
}
}
}
.poke-final {
text-align: center;
&-score {
display: block;
position: relative;
margin-bottom: 1rem;
&::before {
content: '';
position: absolute;
z-index: -1;
top: 50%;
left: 50%;
height: 100px;
width: 100px;
border-radius: 50%;
border: solid ($border-width * 2) $primary-color-dark;
transform: translate(-50%, -50%);
opacity: 0.3;
animation: grow 2s infinite ease-in-out;
}
&-number {
font-size: 8rem;
line-height: 0.4;
color: $primary-color-tint;
}
}
}
.poke-classic {
.poke-image {
&-img {
image-rendering: pixelated;
}
}
}
.animate-section {
&-enter-active,
&-leave-active {
transition: 0.4s ease-in-out;
}
&-enter,
&-leave-to {
opacity: 0;
}
&-enter {
.poke-final-score-number {
transform: translateY(-30px);
}
}
&-leave-active {
transform: translateX(-30%);
}
&-enter-active {
transition-delay: 0.1s;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
}
}
.animate-options {
&-enter-active {
transition: 0.4s ease-in-out;
@for $i from 4 through 8 {
&:nth-child(#{$i}) {
transition-delay: 0.2s * ($i - 4);
}
}
}
&-enter {
transform: rotateX(-45deg);
transform-origin: top center;
opacity: 0;
}
&-leave-active {
position: absolute;
z-index: -1;
transition: 0.8s ease-in-out;
// @for $i from 0 through 3 {
// $child: $i + 1;
// &:nth-child(#{$child}) {
// top: ($options-height + $border-width / 2) * $i;
// }
// }
&[data-index="0"] {
top: 0;
}
&[data-index="1"] {
top: ($options-height + $border-width / 2);
}
&[data-index="2"] {
top: ($options-height + $border-width / 2) * 2;
}
&[data-index="3"] {
top: ($options-height + $border-width / 2) * 3;
}
}
&-leave-to {
opacity: 0;
}
}
@keyframes grow {
0%, 100% { transform: translate(-50%, -50%) scale(1) }
50% { transform: translate(-50%, -50%) scale(0.6) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment