Added a feature to display a dynamic list of pokemon that you can filter by type and generation

TODO: save current filters for page load, add popup page with info, make clickable cards, potential favourites system?
This commit is contained in:
Notoric 2024-03-19 02:39:47 +00:00
parent ed5c27f449
commit 636b43a261
3 changed files with 610 additions and 4 deletions

View File

@ -7,6 +7,24 @@
:root { :root {
--bgColor: #ffffff; --bgColor: #ffffff;
--normal: #999999;
--fighting: #C03028;
--flying: #A890F0;
--poison: #A040A0;
--ground: #E0C068;
--rock: #B8A038;
--bug: #A8B820;
--ghost: #705898;
--steel: #B8B8D0;
--fire: #F08030;
--water: #6890F0;
--grass: #78C850;
--electric: #F8D030;
--psychic: #F85888;
--ice: #98D8D8;
--dragon: #7038F8;
--dark: #41314e;
--fairy: #EE99AC;
} }
@ -82,7 +100,7 @@ h1 {
} }
.black { .black {
background-color: #000; background-color: #000; /* Reference[0] https://www.w3schools.com/howto/howto_css_cutout_text.asp */
color: #fff; color: #fff;
mix-blend-mode: multiply; mix-blend-mode: multiply;
} }
@ -173,6 +191,357 @@ h3 {
border-bottom: none; border-bottom: none;
} }
/* POKEDEX STYLES */
#pokedex {
max-width: 1300px;
margin: auto;
margin-bottom: 50px;
}
#generation-selector, #type-selector {
overflow-x: auto;
}
#pokedex h2 {
text-align: center;
}
#generation-selector {
display: flex;
}
#generation-selector input {
display: none;
}
#generation-selector label {
display: inline-block;
padding: 10px;
margin: 10px;
min-height: 40px;
min-width: 40px;
text-align: center;
text-shadow: black 0px 0px 3px;
line-height: 40px;
border-radius: 30px;
background-color: #202020;
color: white;
cursor: pointer;
transition: background-color 0.5s;
}
#generation-selector label:hover {
background-color: #303030;
}
#generation-selector input:checked + label {
background-color: var(--bgColor);
}
#type-selector {
display: flex;
}
#type-selector input {
display: none;
}
#type-selector label {
display: inline-block;
padding: 10px;
margin: 10px;
height: 40px;
width: 40px;
text-align: center;
line-height: 40px;
border-radius: 30px;
background-color: #202020;
color: white;
cursor: pointer;
transition: background-color 0.5s;
}
#type-selector label img {
width: 40px;
height: 40px;
font-size: 10px;
}
#type-selector label:hover {
background-color: #303030;
}
#type-selector input:checked + label {
background-color: var(--bgColor);
}
#normal:checked + label {
background-color: var(--normal) !important;
}
#fighting:checked + label {
background-color: var(--fighting) !important;
}
#flying:checked + label {
background-color: var(--flying) !important;
}
#poison:checked + label {
background-color: var(--poison) !important;
}
#ground:checked + label {
background-color: var(--ground) !important;
}
#rock:checked + label {
background-color: var(--rock) !important;
}
#bug:checked + label {
background-color: var(--bug) !important;
}
#ghost:checked + label {
background-color: var(--ghost) !important;
}
#steel:checked + label {
background-color: var(--steel) !important;
}
#fire:checked + label {
background-color: var(--fire) !important;
}
#water:checked + label {
background-color: var(--water) !important;
}
#grass:checked + label {
background-color: var(--grass) !important;
}
#electric:checked + label {
background-color: var(--electric) !important;
}
#psychic:checked + label {
background-color: var(--psychic) !important;
}
#ice:checked + label {
background-color: var(--ice) !important;
}
#dragon:checked + label {
background-color: var(--dragon) !important;
}
#dark:checked + label {
background-color: var(--dark) !important;
}
#fairy:checked + label {
background-color: var(--fairy) !important;
}
#pokemon-container .normal-primary-type {
background-color: var(--normal) !important;
}
#pokemon-container .normal-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--normal) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .fighting-primary-type {
background-color: var(--fighting) !important;
}
#pokemon-container .fighting-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--fighting) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .flying-primary-type {
background-color: var(--flying) !important;
}
#pokemon-container .flying-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--flying) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .poison-primary-type {
background-color: var(--poison) !important;
}
#pokemon-container .poison-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--poison) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .ground-primary-type {
background-color: var(--ground) !important;
}
#pokemon-container .ground-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--ground) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .rock-primary-type {
background-color: var(--rock) !important;
}
#pokemon-container .rock-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--rock) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .bug-primary-type {
background-color: var(--bug) !important;
}
#pokemon-container .bug-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--bug) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .ghost-primary-type {
background-color: var(--ghost) !important;
}
#pokemon-container .ghost-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--ghost) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .steel-primary-type {
background-color: var(--steel) !important;
}
#pokemon-container .steel-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--steel) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .fire-primary-type {
background-color: var(--fire) !important;
}
#pokemon-container .fire-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--fire) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .water-primary-type {
background-color: var(--water) !important;
}
#pokemon-container .water-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--water) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .grass-primary-type {
background-color: var(--grass) !important;
}
#pokemon-container .grass-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--grass) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .electric-primary-type {
background-color: var(--electric) !important;
}
#pokemon-container .electric-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--electric) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .psychic-primary-type {
background-color: var(--psychic) !important;
}
#pokemon-container .psychic-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--psychic) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .ice-primary-type {
background-color: var(--ice) !important;
}
#pokemon-container .ice-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--ice) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .dragon-primary-type {
background-color: var(--dragon) !important;
}
#pokemon-container .dragon-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--dragon) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .dark-primary-type {
background-color: var(--dark) !important;
}
#pokemon-container .dark-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--dark) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container .fairy-primary-type {
background-color: var(--fairy) !important;
}
#pokemon-container .fairy-secondary-type {
box-shadow: inset 0px -150px 100px -100px var(--fairy) , 0px 0px 0px 0px transparent !important;
}
#pokemon-container {
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
width: 100%;
height: 220px;
}
.pokemon-card {
height: 200px;
min-width: 300px;
background-color: #444;
margin: 10px;
border-radius: 40px;
overflow: hidden;
}
.pokemon-card img {
position: relative;
top: -120px;
left: 100px;
height: 192px;
width: 192px;
margin: 10px;
image-rendering: pixelated;
}
.pokemon-card .pokemon-name {
font-weight: 500;
font-size: 1.15em;
position: relative;
margin: 0;
top: 15px;
left: 15px;
z-index: 1;
text-shadow: black 1px 1px 3px;
}
.pokemon-card .pokemon-id {
font-weight: 100;
font-size: 3.5em;
position: relative;
margin: 0;
top: 15px;
left: 15px;
z-index: 0;
text-shadow: #0008 1px 1px 2px;
}
/* CAROUSEL STYLES */ /* CAROUSEL STYLES */
#carousel { #carousel {
@ -344,4 +713,10 @@ h3 {
grid-row: 3 / 3; grid-row: 3 / 3;
} }
/* POKEDEX STYLES */
#generation-selector {
justify-content: center;
}
} }

View File

@ -19,6 +19,13 @@
</div> </div>
<div id="below-landing"> <div id="below-landing">
<section id="pokedex">
<h2>Pokedex</h2>
<form id="generation-selector"></form>
<div id="pokemon-container"></div>
<div id="type-selector"></div>
</section>
<section id="features-intro" class="content-column features"> <section id="features-intro" class="content-column features">
<h2>Features</h2> <h2>Features</h2>
<p> <p>

View File

@ -1,4 +1,5 @@
console.log("hello"); let controller = new AbortController(); // Reference[1] https://levelup.gitconnected.com/asynchronous-tasks-got-you-down-heres-how-to-cancel-them-480801e69ae5
let signal = controller.signal;
// COLOUR PICKER BACKGROUND // COLOUR PICKER BACKGROUND
@ -175,6 +176,227 @@ function transitionLake() {
transitionBackground("assets/photos/hintersee-3601004.jpg", "white"); transitionBackground("assets/photos/hintersee-3601004.jpg", "white");
} }
// POKEDEX
async function initPokedex() {
await populateGenerations();
await populateTypes();
populatePokedex(signal);
}
async function populateGenerations() {
const container = document.getElementById("generation-selector");
const url = "https://pokeapi.co/api/v2/generation/";
const response = await fetch(url);
const data = await response.json();
let generations = [];
let generationsURL = [];
const genArray = data.results;
for (let i = 0; i < genArray.length; i++) {
const gen = genArray[i];
const genName = gen.name;
const genId = genName.split("-")[1].toUpperCase();
generations.push(genId);
generationsURL.push(gen.url);
}
for (let i = 0; i < generations.length; i++) {
const gen = generations[i];
const genButton = document.createElement("input");
genButton.type = "checkbox";
genButton.value = generationsURL[i];
genButton.id = `gen${gen}`;
genButton.checked = true;
container.appendChild(genButton);
const genLabel = document.createElement("label");
genLabel.htmlFor = `gen${gen}`;
genLabel.innerHTML = `${gen}`;
container.appendChild(genLabel);
}
}
function getSelectedGenerations() {
const container = document.getElementById("generation-selector");
const checkboxes = container.getElementsByTagName("input");
let selected = [];
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
selected.push(checkboxes[i].value);
}
}
return(selected);
}
async function populateTypes() {
const container = document.getElementById("type-selector");
const url = "https://pokeapi.co/api/v2/type";
const response = await fetch(url);
const data = await response.json();
let types = [];
const typeArray = data.results;
for (let i = 0; i < typeArray.length; i++) {
const type = typeArray[i];
const typeResponse = await fetch(type.url);
const typeData = await typeResponse.json();
if (typeData.id < 1000) {
types.push(type.name);
}
}
for (let i = 0; i < types.length; i++) {
const type = types[i];
const typeButton = document.createElement("input");
typeButton.type = "checkbox";
typeButton.value = type;
typeButton.id = type;
typeButton.checked = true;
container.appendChild(typeButton);
const typeImg = document.createElement("img");
typeImg.src = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${type}.svg`;
typeImg.alt = `${type}`;
const typeLabel = document.createElement("label");
typeLabel.htmlFor = type;
typeLabel.appendChild(typeImg);
container.appendChild(typeLabel);
}
}
function getSelectedTypes() {
const container = document.getElementById("type-selector");
const checkboxes = container.getElementsByTagName("input");
let selected = [];
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
selected.push(checkboxes[i].value);
}
}
return selected;
}
async function populatePokedex(signal) {
const container = document.getElementById("pokemon-container");
container.innerHTML = "";
const types = await getSelectedTypes();
const generations = await getSelectedGenerations();
for (let i = 0; i < generations.length; i++) {
if (signal.aborted) {
return;
}
const url = generations[i];
const response = await fetch(url);
const data = await response.json();
const pokemon = data.pokemon_species;
for (let j = 0; j < pokemon.length; j++) {
if (signal.aborted) {
return;
}
const poke = pokemon[j];
const speciesURL = poke.url;
const speciesResponse = await fetch(speciesURL);
const speciesData = await speciesResponse.json();
const varieties = speciesData.varieties;
for (let k = 0; k < varieties.length; k++) {
if (signal.aborted) {
return;
}
const variety = varieties[k];
const varietyURL = variety.pokemon.url;
const varietyResponse = await fetch(varietyURL);
const varietyData = await varietyResponse.json();
const varietyTypes = varietyData.types;
if (types.includes(varietyTypes[0].type.name)) {
if (signal.aborted) {
return;
}
addPokemon(varietyData);
} else {
try {
if (types.includes(varietyTypes[1].type.name)) {
if (signal.aborted) {
return;
}
addPokemon(varietyData);
}
} catch {
//no second type
}
}
}
}
}
}
function addPokemon(pokemonData) {
const container = document.getElementById("pokemon-container");
// if (container.childElementCount > 100) {
// return;
// }
const pokemon = document.createElement("div");
pokemon.className = "pokemon-card";
pokemon.classList.add(pokemonData.types[0].type.name + "-primary-type");
if (pokemonData.types[1]) {
pokemon.classList.add(pokemonData.types[1].type.name + "-secondary-type");
}
pokemon.id = pokemonData.name;
const pokemonName = document.createElement("p");
pokemonName.className = "pokemon-name";
let formatedName = "";
pokemonData.name.split("-").forEach((word) => {
formatedName += word.charAt(0).toUpperCase() + word.slice(1) + " ";
});
if (formatedName.length > 20) {
formatedName = formatedName.slice(0, formatedName.length - 1);
formatedName = formatedName.slice(0, formatedName.lastIndexOf(" "));
}
pokemonName.innerHTML = formatedName;
pokemon.appendChild(pokemonName);
const pokemonId = document.createElement("p");
pokemonId.className = "pokemon-id";
pokemonId.innerHTML = `#${pokemonData.id}`;
pokemon.style.order = pokemonData.id;
pokemon.appendChild(pokemonId);
const pokemonImg = document.createElement("img");
pokemonImg.src = pokemonData.sprites.front_default;
pokemonImg.alt = pokemonData.name;
pokemon.appendChild(pokemonImg);
container.appendChild(pokemon);
}
const generationSelector = document.getElementById("generation-selector");
generationSelector.addEventListener("change", (event) => {
restartPokedexUpdate();
});
const typeSelector = document.getElementById("type-selector");
typeSelector.addEventListener("change", (event) => {
restartPokedexUpdate();
});
function restartPokedexUpdate() {
controller.abort();
setTimeout(() => {
controller = new AbortController();
signal = controller.signal;
populatePokedex(signal);
}, 250);
}
// ACCORDIAN IMAGES // ACCORDIAN IMAGES
function carouselExpand(i) { function carouselExpand(i) {
@ -206,7 +428,7 @@ function initCarousel() {
// INITIALIZATION // INITIALIZATION
// Set background colour to last saved value // Set background colour to last saved value
const bgColor = document.getElementById("bgColour"); const bgColor = document.getElementById("bgColour");
bgColor.value = localStorage.getItem("bgColour") || "#ffffff"; bgColor.value = localStorage.getItem("bgColour") || "#ff0070";
const initR = parseInt(bgColour.value.substring(1, 3), 16); const initR = parseInt(bgColour.value.substring(1, 3), 16);
const initG = parseInt(bgColour.value.substring(3, 5), 16); const initG = parseInt(bgColour.value.substring(3, 5), 16);
const initB = parseInt(bgColour.value.substring(5, 7), 16); const initB = parseInt(bgColour.value.substring(5, 7), 16);
@ -214,4 +436,6 @@ changeBackground(initR, initG, initB);
document.documentElement.style.setProperty("--bgColor", bgColour.value); document.documentElement.style.setProperty("--bgColor", bgColour.value);
// Initialize carousel // Initialize carousel
initCarousel(); initCarousel();
carouselExpand(1); carouselExpand(1);
// Initialize pokedex
initPokedex();