From 636b43a2614bb810b0952000b79953fa2f06e2be Mon Sep 17 00:00:00 2001 From: Notoric Date: Tue, 19 Mar 2024 02:39:47 +0000 Subject: [PATCH 1/6] 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? --- css/styles.css | 377 ++++++++++++++++++++++++++++++++++++++++++++++++- index.html | 7 + js/scripts.js | 230 +++++++++++++++++++++++++++++- 3 files changed, 610 insertions(+), 4 deletions(-) diff --git a/css/styles.css b/css/styles.css index 918573c..9e0aef7 100644 --- a/css/styles.css +++ b/css/styles.css @@ -7,6 +7,24 @@ :root { --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 { - background-color: #000; + background-color: #000; /* Reference[0] https://www.w3schools.com/howto/howto_css_cutout_text.asp */ color: #fff; mix-blend-mode: multiply; } @@ -173,6 +191,357 @@ h3 { 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 { @@ -344,4 +713,10 @@ h3 { grid-row: 3 / 3; } + /* POKEDEX STYLES */ + + #generation-selector { + justify-content: center; + } + } diff --git a/index.html b/index.html index a86b839..ff46785 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,13 @@
+
+

Pokedex

+
+
+
+
+

Features

diff --git a/js/scripts.js b/js/scripts.js index 41b87f1..e7ef355 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -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 @@ -175,6 +176,227 @@ function transitionLake() { 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 function carouselExpand(i) { @@ -206,7 +428,7 @@ function initCarousel() { // INITIALIZATION // Set background colour to last saved value 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 initG = parseInt(bgColour.value.substring(3, 5), 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); // Initialize carousel initCarousel(); -carouselExpand(1); \ No newline at end of file +carouselExpand(1); +// Initialize pokedex +initPokedex(); \ No newline at end of file From caf9ec9187a0841df22be84e983ca72f97c0e721 Mon Sep 17 00:00:00 2001 From: Notoric Date: Mon, 15 Apr 2024 17:21:08 +0100 Subject: [PATCH 2/6] Pokemon Popup w/ statblock --- css/styles.css | 231 +++++++++++++++++++++++++++++++++++++++---------- index.html | 2 +- js/scripts.js | 131 +++++++++++++++++++++++++++- 3 files changed, 317 insertions(+), 47 deletions(-) diff --git a/css/styles.css b/css/styles.css index 9e0aef7..e99600a 100644 --- a/css/styles.css +++ b/css/styles.css @@ -51,7 +51,7 @@ body { } #landing { - width: 100vw; + width: 100%; height: 100vh; display: flex; justify-content: center; @@ -87,6 +87,7 @@ h1 { font-weight: 600; text-align: center; user-select: none; + white-space: nowrap; } #titleBanner { @@ -138,7 +139,7 @@ h1 { background-color: #101010; overflow: auto; color: white; - padding: 50px; + padding: 10px; } p { @@ -191,10 +192,118 @@ h3 { border-bottom: none; } +/* FULLSCREEN STYLES */ +#fullscreen { + position: fixed; + height: 100vh; + width: 100%; + background: #1118; + z-index: 100; + backdrop-filter: blur(10px); + top: 0px; + left: 0px; + display: none; +} + +#fullscreen.visible { + display: block; +} + +#pokemon-display { + height: calc(100% - 70px); + width: calc(100% - 70px); + background: white; + margin: 35px; + display: flex; + border-radius: 25px; +} + +#fullscreen #close-button { + position: absolute; + top: 2px; + right: 0px; + height: 40px; + width: 40px; + border: 0; + background: #fff0; + font-size: 1.5em; + cursor: pointer; + color: white; + text-shadow: black 1px 1px 3px; +} + +#pokemon-display img { + max-height: 475px; + max-width: 475px; +} + +#statblock .stat-empty { + color: #bbb; +} + +#statblock #hp .stat-filled { + color: #90d060; +} + +#statblock #attack .stat-filled { + color: #ffd060; +} + +#statblock #defense .stat-filled { + color: #f09040; +} + +#statblock #special-attack .stat-filled { + color: #40a0f0; +} + +#statblock #special-defense .stat-filled { + color: #a030ff; +} + +#statblock #speed .stat-filled { + color: #ff40ff; +} + +#statblock { + background-color: #222; + color: white; + display: flex; + flex-direction: column; + align-items: center; + padding: 10px; + height: fit-content; +} + +.stat-bar { + display: grid; + margin-bottom: 8px; +} + +#statblock .stat-name { + grid-column: 1; + grid-row: 1; +} + +#statblock .stat-value { + grid-column: 2; + grid-row: 1; + text-align: right; +} + +#statblock .stat-graphic { + grid-column: 1 / span 2; + grid-row: 2; +} + +#statblock p { + margin: 0; +} + /* POKEDEX STYLES */ #pokedex { - max-width: 1300px; + max-width: 1440px; margin: auto; margin-bottom: 50px; } @@ -348,147 +457,147 @@ h3 { background-color: var(--fairy) !important; } -#pokemon-container .normal-primary-type { +.normal-primary-type { background-color: var(--normal) !important; } -#pokemon-container .normal-secondary-type { +.normal-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--normal) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .fighting-primary-type { +.fighting-primary-type { background-color: var(--fighting) !important; } -#pokemon-container .fighting-secondary-type { +.fighting-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--fighting) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .flying-primary-type { +.flying-primary-type { background-color: var(--flying) !important; } -#pokemon-container .flying-secondary-type { +.flying-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--flying) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .poison-primary-type { +.poison-primary-type { background-color: var(--poison) !important; } -#pokemon-container .poison-secondary-type { +.poison-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--poison) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .ground-primary-type { +.ground-primary-type { background-color: var(--ground) !important; } -#pokemon-container .ground-secondary-type { +.ground-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--ground) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .rock-primary-type { +.rock-primary-type { background-color: var(--rock) !important; } -#pokemon-container .rock-secondary-type { +.rock-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--rock) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .bug-primary-type { +.bug-primary-type { background-color: var(--bug) !important; } -#pokemon-container .bug-secondary-type { +.bug-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--bug) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .ghost-primary-type { +.ghost-primary-type { background-color: var(--ghost) !important; } -#pokemon-container .ghost-secondary-type { +.ghost-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--ghost) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .steel-primary-type { +.steel-primary-type { background-color: var(--steel) !important; } -#pokemon-container .steel-secondary-type { +.steel-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--steel) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .fire-primary-type { +.fire-primary-type { background-color: var(--fire) !important; } -#pokemon-container .fire-secondary-type { +.fire-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--fire) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .water-primary-type { +.water-primary-type { background-color: var(--water) !important; } -#pokemon-container .water-secondary-type { +.water-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--water) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .grass-primary-type { +.grass-primary-type { background-color: var(--grass) !important; } -#pokemon-container .grass-secondary-type { +.grass-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--grass) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .electric-primary-type { +.electric-primary-type { background-color: var(--electric) !important; } -#pokemon-container .electric-secondary-type { +.electric-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--electric) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .psychic-primary-type { +.psychic-primary-type { background-color: var(--psychic) !important; } -#pokemon-container .psychic-secondary-type { +.psychic-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--psychic) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .ice-primary-type { +.ice-primary-type { background-color: var(--ice) !important; } -#pokemon-container .ice-secondary-type { +.ice-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--ice) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .dragon-primary-type { +.dragon-primary-type { background-color: var(--dragon) !important; } -#pokemon-container .dragon-secondary-type { +.dragon-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--dragon) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .dark-primary-type { +.dark-primary-type { background-color: var(--dark) !important; } -#pokemon-container .dark-secondary-type { +.dark-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--dark) , 0px 0px 0px 0px transparent !important; } -#pokemon-container .fairy-primary-type { +.fairy-primary-type { background-color: var(--fairy) !important; } -#pokemon-container .fairy-secondary-type { +.fairy-secondary-type { box-shadow: inset 0px -150px 100px -100px var(--fairy) , 0px 0px 0px 0px transparent !important; } @@ -498,7 +607,7 @@ h3 { overflow-x: auto; overflow-y: hidden; width: 100%; - height: 220px; + height: 230px; } .pokemon-card { @@ -518,6 +627,7 @@ h3 { width: 192px; margin: 10px; image-rendering: pixelated; + color: black; } .pokemon-card .pokemon-name { @@ -648,11 +758,10 @@ h3 { } .carousel-item { - min-width: 200px; height: 800px; - width: 150px; - min-width: 150px; - border-radius: 74px; + width: 146px; + min-width: 146px; + border-radius: 75px; } .carousel-item .carousel-id { @@ -674,12 +783,36 @@ h3 { } .content-column { - max-width: 1300px; + max-width: 1440px; margin: auto; } + /* FULLSCREEN STYLES */ + + #pokemon-display { + height: calc(100% - 200px); + width: calc(100% - 200px); + margin: 100px; + border-radius: 60px; + } + + #fullscreen #close-button { + top: 20px; + right: 20px; + height: 60px; + width: 60px; + border-radius: 60px; + font-size: 2em; + } + + + /* BELOW LANDING STYLES */ + #below-landing { + padding: 50px; + } + .feature-showcase { display: grid; grid-template-columns: 1fr 1fr; @@ -720,3 +853,11 @@ h3 { } } + +h1 { + margin: auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/index.html b/index.html index ff46785..8cd3f26 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,7 @@

- +

Pokedex

diff --git a/js/scripts.js b/js/scripts.js index e7ef355..1632c99 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -375,6 +375,11 @@ function addPokemon(pokemonData) { pokemonImg.alt = pokemonData.name; pokemon.appendChild(pokemonImg); + pokemon.addEventListener("click", () => { + const id = pokemonData.id; + createPokemonDisplay(id); + }); + container.appendChild(pokemon); } @@ -397,6 +402,115 @@ function restartPokedexUpdate() { }, 250); } +async function createPokemonDisplay(id) { + const fullscreenContainer = document.getElementById("fullscreen"); + fullscreenContainer.className = "visible"; + + const pokemonDisplay = document.createElement("div"); + pokemonDisplay.id = "pokemon-display"; + + const closeButton = document.createElement("button"); + closeButton.id = "close-button"; + closeButton.innerHTML = "X"; + closeButton.addEventListener("click", () => { + closePokemonDisplay(); + }); + + fullscreenContainer.addEventListener("click", (event) => { + if (event.target.id === "fullscreen") { + closePokemonDisplay(); + } + }); + + const url = `https://pokeapi.co/api/v2/pokemon/${id}`; + const response = await fetch(url); + const data = await response.json(); + + pokemonDisplay.className = data.types[0].type.name + "-primary-type" + + const pokemonName = document.createElement("p"); + pokemonName.className = "pokemon-name"; + + let formatedName = ""; + data.name.split("-").forEach((word) => { + formatedName += word.charAt(0).toUpperCase() + word.slice(1) + " "; + }); + formatedName = formatedName.slice(0, formatedName.length - 1); + + pokemonName.innerHTML = formatedName; + + const pokemonId = document.createElement("p"); + pokemonId.className = "pokemon-id"; + pokemonId.innerHTML = `#${data.id}`; + + const pokemonImg = document.createElement("img"); + pokemonImg.src = data.sprites.other["official-artwork"].front_default; + pokemonImg.alt = data.name; + + const pokemonStatblock = document.createElement("div"); + pokemonStatblock.id = "statblock"; + + data.stats.forEach((stat) => { + const statBar = document.createElement("div"); + statBar.className = "stat-bar"; + statBar.id = stat.stat.name; + const statName = document.createElement("p"); + statName.className = "stat-name"; + let formatedStatName = ""; + stat.stat.name.split("-").forEach((word) => { + formatedStatName += word.charAt(0).toUpperCase() + word.slice(1) + " "; + }); + formatedStatName = formatedStatName.slice(0, formatedStatName.length - 1); + + statName.innerHTML = formatedStatName; + const statValue = document.createElement("p"); + statValue.className = "stat-value"; + statValue.innerHTML = stat.base_stat; + + const statGraphic = document.createElement("p"); + statGraphic.className = "stat-graphic"; + let stringFilled = document.createElement("span"); + let stringEmpty = document.createElement("span"); + + const barSize = 25; + + const baseStat = (stat.base_stat / 255) * barSize; + const emptyStat = barSize - baseStat; + + stringFilled.innerHTML = "❚".repeat(baseStat); + stringEmpty.innerHTML = "❚".repeat(emptyStat); + + stringFilled.className = "stat-filled"; + stringEmpty.className = "stat-empty"; + + statGraphic.appendChild(stringFilled); + statGraphic.appendChild(stringEmpty); + + statBar.appendChild(statGraphic); + statBar.appendChild(statName); + statBar.appendChild(statValue); + pokemonStatblock.appendChild(statBar); + }); + + pokemonDisplay.appendChild(pokemonName); + pokemonDisplay.appendChild(pokemonId); + pokemonDisplay.appendChild(pokemonImg); + pokemonDisplay.appendChild(pokemonStatblock); + + fullscreenContainer.appendChild(pokemonDisplay); + fullscreenContainer.appendChild(closeButton); +} + +function closePokemonDisplay() { + const fullscreenContainer = document.getElementById("fullscreen"); + fullscreenContainer.className = ""; + const children = Array.from(fullscreenContainer.children); + children.forEach((child) => { + fullscreenContainer.removeChild(child); + }); + +} + // ACCORDIAN IMAGES function carouselExpand(i) { @@ -438,4 +552,19 @@ document.documentElement.style.setProperty("--bgColor", bgColour.value); initCarousel(); carouselExpand(1); // Initialize pokedex -initPokedex(); \ No newline at end of file +initPokedex(); + +function handleMouseMove(event) { + const width = document.body.clientWidth; + const height = document.body.clientHeight; + + const xAbsolute = event.clientX / width; + const yAbsolute = event.clientY / height; + + const maxOffset = 40; + const xOffset = (xAbsolute * (2 * maxOffset)) - maxOffset; + const yOffset = (yAbsolute * (2 * maxOffset)) - maxOffset; + + document.body.style.backgroundPosition = `${xOffset}px ${yOffset}px`; + } + From 616acf0074fb0c52976be1319b574d925df39cea Mon Sep 17 00:00:00 2001 From: Notoric Date: Mon, 15 Apr 2024 20:26:18 +0100 Subject: [PATCH 3/6] Added better resizing for tablet, phone and pc --- css/styles.css | 157 +++++++++++++++++++++++++++++++++++++++++++++---- js/scripts.js | 11 +++- 2 files changed, 153 insertions(+), 15 deletions(-) diff --git a/css/styles.css b/css/styles.css index e99600a..521cb6c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -206,16 +206,25 @@ h3 { } #fullscreen.visible { - display: block; + display: flex; } #pokemon-display { - height: calc(100% - 70px); - width: calc(100% - 70px); + height: calc(100% - 90px); + width: calc(100% - 90px); background: white; - margin: 35px; + margin: auto; + align-self: center; display: flex; - border-radius: 25px; + flex-direction: column; + border-radius: 26px; + padding: 10px; + overflow-y: scroll; + min-width: 260px; +} + +#pokemon-display::-webkit-scrollbar { + display: none; } #fullscreen #close-button { @@ -232,9 +241,52 @@ h3 { text-shadow: black 1px 1px 3px; } -#pokemon-display img { +#pokemon-display #image-container { + display: flex; + justify-content: center; + align-items: center; max-height: 475px; - max-width: 475px; + background: #ddd; + background-image: linear-gradient(to right, transparent 0px, #999 1px, transparent 2px), linear-gradient(to bottom, transparent 0px, #999 1px, transparent 2px); + background-size: 35px 35px; + animation: transform 2.5s infinite; + animation-timing-function: linear; + border-radius: 15px; + margin-bottom: 8px; +} + +#pokemon-display img { + width: 100%; +} + +@keyframes transform { + from { + background-position: 0px 0px; + } + to { + background-position: 35px 35px; + } +} + +#pokemon-display .pokemon-name { + font-size: 1.2em; + font-weight: 500; + margin: 0; + margin-left: 10px; + margin-bottom: 10px; + text-align: center; +} + +#pokemon-display .pokemon-id { + font-size: 1.6em; + font-weight: 150; + margin: 0; + margin-left: 10px; + position: relative; + overflow: visible; + height: 0; + width: 0; + text-shadow: #fff 0px 0px 5px; } #statblock .stat-empty { @@ -258,7 +310,7 @@ h3 { } #statblock #special-defense .stat-filled { - color: #a030ff; + color: #6b30ff; } #statblock #speed .stat-filled { @@ -273,6 +325,7 @@ h3 { align-items: center; padding: 10px; height: fit-content; + border-radius: 15px; } .stat-bar { @@ -300,6 +353,12 @@ h3 { margin: 0; } +#image-and-stats { + width: 100%; + display: flex; + flex-direction: column; +} + /* POKEDEX STYLES */ #pokedex { @@ -739,6 +798,44 @@ h3 { /* applies to screens wider than 499px */ + /* FULLSCREEN STYLES */ + #pokemon-display { + height: calc(100% - 120px); + width: calc(100% - 120px); + border-radius: 30px; + padding: 20px; + max-height: 600px; + max-width: 900px; + } + + #image-and-stats { + width: 100%; + display: grid; + grid-template-columns: 1fr 280px; + + } + + #pokemon-display img { + grid-column: 1; + max-height: 440px; + max-width: 440px; + justify-self: center; + } + + #pokemon-display #image-container { + display: flex; + grid-column: 1; + width: calc(100% - 10px); + justify-content: center; + margin: 0; + margin-right: 10px; + } + + #statblock { + grid-column: 2; + width: 260px; + } + } @@ -790,10 +887,10 @@ h3 { /* FULLSCREEN STYLES */ #pokemon-display { - height: calc(100% - 200px); - width: calc(100% - 200px); - margin: 100px; - border-radius: 60px; + border-radius: 35px; + padding: 20px; + max-height: 755px; + max-width: 1120px; } #fullscreen #close-button { @@ -801,11 +898,45 @@ h3 { right: 20px; height: 60px; width: 60px; - border-radius: 60px; font-size: 2em; } + #pokemon-display .pokemon-name { + font-size: 2.2em; + text-align: left; + margin-bottom: 5px; + transform: translate(0px, -8px); + } + #pokemon-display .pokemon-id { + font-size: 2.5em; + } + + #pokemon-display img { + max-height: 475px; + max-width: 475px; + height: 475px; + width: 475px; + } + + #pokemon-display #image-container { + max-height: 475px; + max-width: 475px; + height: 475px; + width: 475px; + margin: 0; + margin-bottom: 8px; + } + + #statblock { + flex-direction: row; + flex-wrap: wrap; + width: 455px; + font-size: 0.85em !important; + justify-content: space-between; + grid-column: 1; + grid-row: 2; + } /* BELOW LANDING STYLES */ diff --git a/js/scripts.js b/js/scripts.js index 1632c99..f402600 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -446,6 +446,9 @@ async function createPokemonDisplay(id) { const pokemonImg = document.createElement("img"); pokemonImg.src = data.sprites.other["official-artwork"].front_default; pokemonImg.alt = data.name; + const pokemonImgContainer = document.createElement("div"); + pokemonImgContainer.id = "image-container"; + pokemonImgContainer.appendChild(pokemonImg); const pokemonStatblock = document.createElement("div"); pokemonStatblock.id = "statblock"; @@ -492,10 +495,14 @@ async function createPokemonDisplay(id) { pokemonStatblock.appendChild(statBar); }); + const imageAndStats = document.createElement("div"); + imageAndStats.id = "image-and-stats"; + imageAndStats.appendChild(pokemonImgContainer); + imageAndStats.appendChild(pokemonStatblock); + pokemonDisplay.appendChild(pokemonName); pokemonDisplay.appendChild(pokemonId); - pokemonDisplay.appendChild(pokemonImg); - pokemonDisplay.appendChild(pokemonStatblock); + pokemonDisplay.appendChild(imageAndStats); fullscreenContainer.appendChild(pokemonDisplay); fullscreenContainer.appendChild(closeButton); From 6c18a0f3d3b5f812dd7431ecae59a1c93ca0113b Mon Sep 17 00:00:00 2001 From: Notoric Date: Tue, 16 Apr 2024 17:17:18 +0100 Subject: [PATCH 4/6] Added text entry info box --- css/styles.css | 74 ++++++++++++++++++++++++ js/scripts.js | 152 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 209 insertions(+), 17 deletions(-) diff --git a/css/styles.css b/css/styles.css index 521cb6c..f859dd5 100644 --- a/css/styles.css +++ b/css/styles.css @@ -259,6 +259,20 @@ h3 { width: 100%; } +#description-carousel { + display: flex; + flex-direction: row; + margin: 10px; + background: #222; + border-radius: 15px; + padding-left: 28px; + padding-right: 28px; +} + +.description-carousel-item { + color: white; +} + @keyframes transform { from { background-position: 0px 0px; @@ -901,6 +915,12 @@ h3 { font-size: 2em; } + #pokemon-contents { + display: grid; + grid-template-columns: 475px 1fr; + grid-column-gap: 8px; + } + #pokemon-display .pokemon-name { font-size: 2.2em; text-align: left; @@ -938,6 +958,60 @@ h3 { grid-row: 2; } + #description-carousel { + margin: 0; + overflow: hidden; + height: fit-content; + position: relative; + } + + #description-carousel .description-carousel-item { + width: 100%; + flex: 0 0 100%; + height: fit-content; + margin-right: 33px; + transition: transform 0.3s ease; + } + + #description-carousel #carousel-left-button { + top: 0; + left: 0; + text-align: left; + } + + #description-carousel #carousel-right-button { + top: 0; + right: 0; + text-align: right; + } + + #description-carousel button { + position: absolute; + height: 100%; + border: none; + background: #0000; + color: white; + width: 30px; + cursor: pointer; + } + + #description-carousel button:hover { + font-size: 16px; + } + + #description-carousel .description-text { + left: 0; + color: #ccc; + } + + #description-carousel .description-game { + left: 0; + font-size: 1em; + font-weight: 400; + padding-bottom: 10px; + border-bottom: 1px solid #ccc; + } + /* BELOW LANDING STYLES */ #below-landing { diff --git a/js/scripts.js b/js/scripts.js index f402600..66165af 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -1,5 +1,6 @@ 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; +let activePokemonCarousel = 0; // COLOUR PICKER BACKGROUND @@ -403,6 +404,7 @@ function restartPokedexUpdate() { } async function createPokemonDisplay(id) { + activePokemonCarousel = 0; const fullscreenContainer = document.getElementById("fullscreen"); fullscreenContainer.className = "visible"; @@ -425,6 +427,10 @@ async function createPokemonDisplay(id) { const url = `https://pokeapi.co/api/v2/pokemon/${id}`; const response = await fetch(url); const data = await response.json(); + + const speciesurl = data.species.url; + const speciesResponse = await fetch(speciesurl); + const speciesData = await speciesResponse.json(); pokemonDisplay.className = data.types[0].type.name + "-primary-type" @@ -500,9 +506,87 @@ async function createPokemonDisplay(id) { imageAndStats.appendChild(pokemonImgContainer); imageAndStats.appendChild(pokemonStatblock); + const descriptionCarousel = document.createElement("div"); + descriptionCarousel.id = "description-carousel"; + + let descriptionArray = []; + + speciesData.flavor_text_entries.forEach((entry) => { + if (entry.language.name === "en") { + const game = entry.version.name; + const pokemonDescription = entry.flavor_text; + descriptionArray[game] = pokemonDescription; + } + }); + + let descriptionArrayFiltered = [] + let gameArrayFiltered = [] + + for (const game in descriptionArray) { + let index = descriptionArrayFiltered.indexOf(descriptionArray[game]); + if (index === -1) { + descriptionArrayFiltered.push(descriptionArray[game]); + gameArrayFiltered.push(game); + } else { + gameArrayFiltered[index] += " | " + game; + } + } + + descriptionArrayFiltered.forEach((description, i) => { + const carouselItem = document.createElement("div"); + carouselItem.className = "description-carousel-item"; + + let descriptionFormatted = description; + descriptionFormatted = descriptionFormatted.replace("\u000c", " "); + descriptionFormatted = descriptionFormatted.replace("\n", " "); + + const descriptionText = document.createElement("p"); + descriptionText.className = "description-text"; + descriptionText.innerHTML = descriptionFormatted; + + const descriptionGame = document.createElement("p"); + descriptionGame.className = "description-game"; + descriptionGame.innerHTML = gameArrayFiltered[i].toUpperCase(); + + carouselItem.appendChild(descriptionGame); + carouselItem.appendChild(descriptionText); + descriptionCarousel.appendChild(carouselItem); + }); + + const leftButton = document.createElement("button"); + leftButton.id = "carousel-left-button"; + leftButton.innerHTML = "◀ "; + leftButton.style.display = "none"; + + const rightButton = document.createElement("button"); + rightButton.id = "carousel-right-button"; + rightButton.innerHTML = " ▶"; + + leftButton.addEventListener("click", () => { + pokemonCarousel("left"); + }); + + rightButton.addEventListener("click", () => { + pokemonCarousel("right"); + }); + + descriptionCarousel.appendChild(leftButton); + descriptionCarousel.appendChild(rightButton); + + window.addEventListener("resize", () => { + transitionPokemonCarousel(activePokemonCarousel); + }); + + const pokemonContents = document.createElement("div"); + pokemonContents.id = "pokemon-contents"; + pokemonDisplay.appendChild(pokemonName); pokemonDisplay.appendChild(pokemonId); - pokemonDisplay.appendChild(imageAndStats); + + pokemonContents.appendChild(imageAndStats); + pokemonContents.appendChild(descriptionCarousel); + + pokemonDisplay.appendChild(pokemonContents); fullscreenContainer.appendChild(pokemonDisplay); fullscreenContainer.appendChild(closeButton); @@ -518,6 +602,55 @@ function closePokemonDisplay() { } +function pokemonCarousel(direction) { + const items = document.getElementsByClassName("description-carousel-item").length; + const leftButton = document.getElementById("carousel-left-button"); + const rightButton = document.getElementById("carousel-right-button"); + + if (items < 2) { + console.log("Not enough items for a carousel"); + return; + } + + if (direction === "left" && activePokemonCarousel === 0) { + console.log("Carousel already too far left"); + return; + } else if (direction === "right" && activePokemonCarousel === items - 1) { + console.log("Carousel already too far right"); + return; + } + + if (direction == "left") { + activePokemonCarousel--; + } else if (direction == "right") { + activePokemonCarousel++; + } + + if (activePokemonCarousel == 0) { + leftButton.style.display = "none"; + } else { + leftButton.style.display = "block"; + } + + if (activePokemonCarousel == items - 1) { + rightButton.style.display = "none"; + } else { + rightButton.style.display = "block"; + } + + transitionPokemonCarousel(activePokemonCarousel); +} + +function transitionPokemonCarousel(i) { + let offset = 0; + const items = document.getElementsByClassName("description-carousel-item"); + const width = items[0].offsetWidth; + offset = (i * (width + 33)); + for (const item of items) { + item.style.transform = `translateX(-${offset}px)`; + } +} + // ACCORDIAN IMAGES function carouselExpand(i) { @@ -559,19 +692,4 @@ document.documentElement.style.setProperty("--bgColor", bgColour.value); initCarousel(); carouselExpand(1); // Initialize pokedex -initPokedex(); - -function handleMouseMove(event) { - const width = document.body.clientWidth; - const height = document.body.clientHeight; - - const xAbsolute = event.clientX / width; - const yAbsolute = event.clientY / height; - - const maxOffset = 40; - const xOffset = (xAbsolute * (2 * maxOffset)) - maxOffset; - const yOffset = (yAbsolute * (2 * maxOffset)) - maxOffset; - - document.body.style.backgroundPosition = `${xOffset}px ${yOffset}px`; - } - +initPokedex(); \ No newline at end of file From aa90918434393f007809a82e38a3494b81752769 Mon Sep 17 00:00:00 2001 From: Notoric Date: Tue, 16 Apr 2024 17:23:52 +0100 Subject: [PATCH 5/6] Added support for mobile and tablet --- css/styles.css | 106 +++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/css/styles.css b/css/styles.css index f859dd5..ba7eb2c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -262,15 +262,62 @@ h3 { #description-carousel { display: flex; flex-direction: row; - margin: 10px; background: #222; border-radius: 15px; padding-left: 28px; padding-right: 28px; + margin: 0; + overflow: hidden; + height: fit-content; + position: relative; } -.description-carousel-item { +#description-carousel .description-carousel-item { color: white; + width: 100%; + flex: 0 0 100%; + height: fit-content; + margin-right: 33px; + transition: transform 0.3s ease; +} + +#description-carousel #carousel-left-button { + top: 0; + left: 0; + text-align: left; +} + +#description-carousel #carousel-right-button { + top: 0; + right: 0; + text-align: right; +} + +#description-carousel button { + position: absolute; + height: 100%; + border: none; + background: #0000; + color: white; + width: 30px; + cursor: pointer; +} + +#description-carousel button:hover { + font-size: 16px; +} + +#description-carousel .description-text { + left: 0; + color: #ccc; +} + +#description-carousel .description-game { + left: 0; + font-size: 1em; + font-weight: 400; + padding-bottom: 10px; + border-bottom: 1px solid #ccc; } @keyframes transform { @@ -371,6 +418,7 @@ h3 { width: 100%; display: flex; flex-direction: column; + margin-bottom: 8px; } /* POKEDEX STYLES */ @@ -958,60 +1006,6 @@ h3 { grid-row: 2; } - #description-carousel { - margin: 0; - overflow: hidden; - height: fit-content; - position: relative; - } - - #description-carousel .description-carousel-item { - width: 100%; - flex: 0 0 100%; - height: fit-content; - margin-right: 33px; - transition: transform 0.3s ease; - } - - #description-carousel #carousel-left-button { - top: 0; - left: 0; - text-align: left; - } - - #description-carousel #carousel-right-button { - top: 0; - right: 0; - text-align: right; - } - - #description-carousel button { - position: absolute; - height: 100%; - border: none; - background: #0000; - color: white; - width: 30px; - cursor: pointer; - } - - #description-carousel button:hover { - font-size: 16px; - } - - #description-carousel .description-text { - left: 0; - color: #ccc; - } - - #description-carousel .description-game { - left: 0; - font-size: 1em; - font-weight: 400; - padding-bottom: 10px; - border-bottom: 1px solid #ccc; - } - /* BELOW LANDING STYLES */ #below-landing { From 43c3459364b102e9a6541f7f43416bd39e880d53 Mon Sep 17 00:00:00 2001 From: Notoric Date: Tue, 16 Apr 2024 21:38:04 +0100 Subject: [PATCH 6/6] Pokedex type chart --- css/styles.css | 126 ++++++++++++++++++++++++++-- index.html | 3 +- js/scripts.js | 220 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 340 insertions(+), 9 deletions(-) diff --git a/css/styles.css b/css/styles.css index ba7eb2c..3d5790c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -221,10 +221,7 @@ h3 { padding: 10px; overflow-y: scroll; min-width: 260px; -} - -#pokemon-display::-webkit-scrollbar { - display: none; + position: relative; } #fullscreen #close-button { @@ -241,6 +238,20 @@ h3 { text-shadow: black 1px 1px 3px; } +#pokemon-display #type-container { + position: absolute; + top: 0; + right: 0; + margin-top: 8px; + margin-right: 10px; +} + +#pokemon-display .type-icon { + width: 40px; + height: 40px; + margin-left: 10px; +} + #pokemon-display #image-container { display: flex; justify-content: center; @@ -255,6 +266,12 @@ h3 { margin-bottom: 8px; } +#pokemon-display #dex-and-type-container { + display: flex; + flex-direction: column; + width: 100%; +} + #pokemon-display img { width: 100%; } @@ -267,9 +284,11 @@ h3 { padding-left: 28px; padding-right: 28px; margin: 0; + margin-bottom: 8px; overflow: hidden; height: fit-content; position: relative; + overflow-y: scroll; } #description-carousel .description-carousel-item { @@ -310,11 +329,13 @@ h3 { #description-carousel .description-text { left: 0; color: #ccc; + font-style: italic; + font-weight: 250; } #description-carousel .description-game { left: 0; - font-size: 1em; + font-size: 1.2em; font-weight: 400; padding-bottom: 10px; border-bottom: 1px solid #ccc; @@ -335,7 +356,7 @@ h3 { margin: 0; margin-left: 10px; margin-bottom: 10px; - text-align: center; + text-align: left; } #pokemon-display .pokemon-id { @@ -421,6 +442,64 @@ h3 { margin-bottom: 8px; } +#type-chart-container { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + width: 100%; + grid-template-rows: 60px 1fr; + background-color: #222; + border-radius: 15px; + color: #ccc; + margin-bottom: 8px; + height: fit-content; +} + +#type-chart-container #type-chart-title { + grid-column: 1 / span 5; + margin: 0; + margin-top: 10px; + margin-left: 10px; + color: white; + font-weight: 400; + text-align: center; + padding-bottom: 5px; + border-bottom: 1px solid #ccc; + font-size: 1.2em; +} + +.type-chart-title { + display: flex; + flex-direction: column; + text-align: center; + height: 40px; + border-bottom: 1px solid #ccc; + justify-content: center; + padding: 2px; +} + +.type-chart-element { + display: flex; + flex-direction: column; + justify-content: center; + color: black; + text-align: center; + margin: 2px; + height: 18px; + border-radius: 9px; +} + +#noeffect-column, #doubleresist-column, #resist-column, #weak-column { + border-right: 1px solid #ccc; +} + +#type-chart-container .type-chart-column { + display: flex; + flex-direction: column; + font-size: 0.6em; + padding: 0; + list-style-type: none; +} + /* POKEDEX STYLES */ #pokedex { @@ -738,6 +817,7 @@ h3 { margin: 10px; border-radius: 40px; overflow: hidden; + cursor: pointer; } .pokemon-card img { @@ -868,13 +948,18 @@ h3 { padding: 20px; max-height: 600px; max-width: 900px; + padding-bottom: 12px; } #image-and-stats { width: 100%; display: grid; grid-template-columns: 1fr 280px; + } + #pokemon-display #type-container { + margin-top: 14px; + margin-right: 20px; } #pokemon-display img { @@ -953,6 +1038,7 @@ h3 { padding: 20px; max-height: 755px; max-width: 1120px; + overflow: hidden; } #fullscreen #close-button { @@ -967,6 +1053,7 @@ h3 { display: grid; grid-template-columns: 475px 1fr; grid-column-gap: 8px; + grid-template-rows: auto; } #pokemon-display .pokemon-name { @@ -980,6 +1067,24 @@ h3 { font-size: 2.5em; } + #pokemon-display #type-container { + margin-top: 15px; + margin-right: 25px; + } + + #pokemon-display .type-icon { + width: 60px; + height: 60px; + } + + #image-and-stats { + width: 475px; + display: flex; + flex-direction: column; + grid-column: 1; + grid-row: 1 / span 2; + } + #pokemon-display img { max-height: 475px; max-width: 475px; @@ -1006,6 +1111,15 @@ h3 { grid-row: 2; } + #description-carousel { + grid-column: 2; + height: 338px; + } + + #type-chart-container { + height: 337px; + } + /* BELOW LANDING STYLES */ #below-landing { diff --git a/index.html b/index.html index 8cd3f26..c09f70c 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,8 @@

Pokedex

-
+
+

Select any creature to view its profile. You can use the top buttons to toggle creatures from different generations or use the bottom buttons to toggle creatures with a specific type.

diff --git a/js/scripts.js b/js/scripts.js index 66165af..40c4e36 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -209,7 +209,11 @@ async function populateGenerations() { genButton.type = "checkbox"; genButton.value = generationsURL[i]; genButton.id = `gen${gen}`; - genButton.checked = true; + if (gen === "I") { + genButton.checked = true; + } else { + genButton.checked = false; + } container.appendChild(genButton); const genLabel = document.createElement("label"); genLabel.htmlFor = `gen${gen}`; @@ -256,7 +260,11 @@ async function populateTypes() { typeButton.type = "checkbox"; typeButton.value = type; typeButton.id = type; - typeButton.checked = true; + if (type === "normal") { + typeButton.checked = true; + } else { + typeButton.checked = false; + } container.appendChild(typeButton); const typeImg = document.createElement("img"); @@ -539,6 +547,7 @@ async function createPokemonDisplay(id) { let descriptionFormatted = description; descriptionFormatted = descriptionFormatted.replace("\u000c", " "); descriptionFormatted = descriptionFormatted.replace("\n", " "); + descriptionFormatted = descriptionFormatted.replace("POKéMON", "Pokémon"); const descriptionText = document.createElement("p"); descriptionText.className = "description-text"; @@ -570,6 +579,82 @@ async function createPokemonDisplay(id) { pokemonCarousel("right"); }); + const types = data.types; + const primaryType = types[0].type.name; + const primaryIcon = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${primaryType}.svg` + let secondaryType = ""; + + if (types[1]) { + secondaryType = types[1].type.name; + secondaryIcon = `https://raw.githubusercontent.com/duiker101/pokemon-type-svg-icons/5781623f147f1bf850f426cfe1874ba56a9b75ee/icons/${secondaryType}.svg` + } else { + secondaryType = "Monotype"; + } + + const typeChartContainer = document.createElement("div"); + typeChartContainer.id = "type-chart-container"; + + const typeChartTitle = document.createElement("p"); + typeChartTitle.id = "type-chart-title"; + typeChartTitle.innerHTML = "Type Chart"; + + const noeffectColumn = document.createElement("ul"); + noeffectColumn.id = "noeffect-column"; + noeffectColumn.className = "type-chart-column"; + const noeffectTitle = document.createElement("li"); + noeffectTitle.innerHTML = "Immune"; + noeffectTitle.className = "type-chart-title"; + noeffectColumn.appendChild(noeffectTitle); + + const doubleresistColumn = document.createElement("ul"); + doubleresistColumn.id = "doubleresist-column"; + doubleresistColumn.className = "type-chart-column"; + const doubleresistTitle = document.createElement("li"); + doubleresistTitle.innerHTML = "Double Resist"; + doubleresistTitle.className = "type-chart-title"; + doubleresistColumn.appendChild(doubleresistTitle); + + const resistColumn = document.createElement("ul"); + resistColumn.id = "resist-column"; + resistColumn.className = "type-chart-column"; + const resistTitle = document.createElement("li"); + resistTitle.innerHTML = "Resist"; + resistTitle.className = "type-chart-title"; + resistColumn.appendChild(resistTitle); + + const weakColumn = document.createElement("ul"); + weakColumn.id = "weak-column"; + weakColumn.className = "type-chart-column"; + const weakTitle = document.createElement("li"); + weakTitle.innerHTML = "Weak"; + weakTitle.className = "type-chart-title"; + weakColumn.appendChild(weakTitle); + + const doubleweakColumn = document.createElement("ul"); + doubleweakColumn.id = "doubleweak-column"; + doubleweakColumn.className = "type-chart-column"; + const doubleweakTitle = document.createElement("li"); + doubleweakTitle.innerHTML = "Double Weak"; + doubleweakTitle.className = "type-chart-title"; + doubleweakColumn.appendChild(doubleweakTitle); + + typeChartContainer.appendChild(typeChartTitle); + typeChartContainer.appendChild(noeffectColumn); + typeChartContainer.appendChild(doubleresistColumn); + typeChartContainer.appendChild(resistColumn); + typeChartContainer.appendChild(weakColumn); + typeChartContainer.appendChild(doubleweakColumn); + + const primaryImg = document.createElement("img"); + primaryImg.src = primaryIcon; + primaryImg.alt = primaryType; + primaryImg.className = "type-icon"; + + const secondaryImg = document.createElement("img"); + secondaryImg.src = secondaryIcon; + secondaryImg.alt = secondaryType; + secondaryImg.className = "type-icon"; + descriptionCarousel.appendChild(leftButton); descriptionCarousel.appendChild(rightButton); @@ -580,16 +665,33 @@ async function createPokemonDisplay(id) { const pokemonContents = document.createElement("div"); pokemonContents.id = "pokemon-contents"; + const typeContainer = document.createElement("div"); + typeContainer.id = "type-container"; + typeContainer.appendChild(primaryImg); + typeContainer.appendChild(secondaryImg); + + // const dexAndTypeContainer = document.createElement("div"); + // dexAndTypeContainer.id = "dex-and-type-container"; + // dexAndTypeContainer.appendChild(descriptionCarousel); + // dexAndTypeContainer.appendChild(typeChartContainer); + pokemonDisplay.appendChild(pokemonName); pokemonDisplay.appendChild(pokemonId); + pokemonDisplay.appendChild(typeContainer); pokemonContents.appendChild(imageAndStats); + //pokemonContents.appendChild(dexAndTypeContainer); pokemonContents.appendChild(descriptionCarousel); + pokemonContents.appendChild(typeChartContainer); + pokemonDisplay.appendChild(pokemonContents); fullscreenContainer.appendChild(pokemonDisplay); fullscreenContainer.appendChild(closeButton); + + const typeChart = await calculateTypeChart(primaryType, secondaryType) + displayTypeChart(typeChart); } function closePokemonDisplay() { @@ -651,6 +753,120 @@ function transitionPokemonCarousel(i) { } } +async function calculateTypeChart(primaryType, secondaryType, stat) { + let typeArray = {"normal": 0, "fighting": 0, "flying": 0, "poison": 0, "ground": 0, "rock": 0, "bug": 0, "ghost": 0, "steel": 0, "fire": 0, "water": 0, "grass": 0, "electric": 0, "psychic": 0, "ice": 0, "dragon": 0, "dark": 0, "fairy": 0}; + + const primaryURL = `https://pokeapi.co/api/v2/type/${primaryType}`; + const primaryResponse = await fetch(primaryURL); + const primaryData = await primaryResponse.json(); + + primaryData.damage_relations.double_damage_from.forEach((type) => { + typeArray[type.name] += 1; + }); + primaryData.damage_relations.half_damage_from.forEach((type) => { + typeArray[type.name] -= 1; + }); + primaryData.damage_relations.no_damage_from.forEach((type) => { + typeArray[type.name] -= 999; + }); + + if (secondaryType !== "Monotype") { + const secondaryURL = `https://pokeapi.co/api/v2/type/${secondaryType}`; + const secondaryResponse = await fetch(secondaryURL); + const secondaryData = await secondaryResponse.json(); + + secondaryData.damage_relations.double_damage_from.forEach((type) => { + typeArray[type.name] += 1; + }); + secondaryData.damage_relations.half_damage_from.forEach((type) => { + typeArray[type.name] -= 1; + }); + secondaryData.damage_relations.no_damage_from.forEach((type) => { + typeArray[type.name] -= 999; + }); + } + + return typeArray; +} + +function displayTypeChart(typeArray) { + const resist = []; + const doubleresist = []; + const weak = []; + const doubleweak = []; + const noeffect = []; + for (const type in typeArray) { + if (typeArray[type] === -1) { + resist.push(type); + } else if (typeArray[type] === -2) { + doubleresist.push(type); + } else if (typeArray[type] === 1) { + weak.push(type); + } + else if (typeArray[type] === 2) { + doubleweak.push(type); + } else if (typeArray[type] < -100) { + noeffect.push(type); + } else { + //neutral + } + } + + const noeffectColumn = document.getElementById("noeffect-column"); + const doubleresistColumn = document.getElementById("doubleresist-column"); + const resistColumn = document.getElementById("resist-column"); + const weakColumn = document.getElementById("weak-column"); + const doubleweakColumn = document.getElementById("doubleweak-column"); + + for (const element of document.getElementsByClassName("type-chart-element")) { + element.remove(); + } + + try { + noeffect.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + noeffectColumn.appendChild(typeElement); + }); + + doubleresist.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + doubleresistColumn.appendChild(typeElement); + }); + + resist.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + resistColumn.appendChild(typeElement); + }); + + weak.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + weakColumn.appendChild(typeElement); + }); + + doubleweak.forEach((type) => { + const typeElement = document.createElement("li"); + typeElement.innerHTML = type.toUpperCase(); + typeElement.className = "type-chart-element"; + typeElement.classList.add(type + "-primary-type"); + doubleweakColumn.appendChild(typeElement); + }); + } catch { + //type elements not built yet + } +} + // ACCORDIAN IMAGES function carouselExpand(i) {