iT邦幫忙

2022 iThome 鐵人賽

DAY 30
0


超白話畫面和功能拆解

  • 背景顏色取決於寶可夢的類型
  • CSS手刻卡片排版
  • 每個卡片內包含寶可夢圖片、名稱、編號、背景顏色、類型,以上這些都要動態呈現
    https://ithelp.ithome.com.tw/upload/images/20221006/201493620iIcviQw00.png

運用知識點羅列

  • CSS
知識點 使用說明
linear-gradient 設定線性的漸層背景
flexbox 在此用於製作卡片的排版且有RWD效果
  • JS
知識點 使用說明
async / await 簡化promise以及對多個 Promise 物件執行某些操作
fetch() 使用 fetch 發送請求 ( request )
Object.keys() 回傳一個包含物件中所有可列舉之屬性陣列
find() 回傳第一個滿足所提供之測試函式的元素值,否則回傳 undefined
map() 遍歷陣列中的元素
slice() 為原陣列選擇之 begin 至 end(不含 end)部分的淺拷貝(shallow copy)
padStart() 將編號的前面自動補0
indexOf() 回傳給定元素於陣列中第一個被找到之索引,若不存在則回傳 -1

流程講解

  • HTML
    這裡先示範三個寶可夢,到時候會刪掉只留下外部的container,並用JS去操控DOM
 <h1>Pokedex</h1>
    <div class="container" id="container">
        <!-- 1 -->
        <div class="pokemon" style="background-color: red;">
            <div class="img-container">
                <img src="..." alt="...">
            </div>
            <div class="info">
                <span class="number">#001</span>  
                <span class="name">皮卡丘</span>
                <small class="type"> <span>電</span> </small>
            </div>
        </div>
        
        <!-- 2 -->
        <div class="pokemon" style="background-color: red;">
            <div class="img-container">
                <img src="..." alt="...">
            </div>
            <div class="info">
                <span class="number">#002</span>
                <span class="name">妙蛙草</span>
                <small class="type"> <span>草</span> </small>
            </div>
        </div>
        
        <!-- 3 -->
        <div class="pokemon" style="background-color: red;">
            <div class="img-container">
                <img src="..." alt="...">
            </div>
            <div class="info">
                <span class="number">#003</span>
                <span class="name">傑尼龜</span>
                <small class="type"> <span>水</span> </small>
            </div>
        </div>
    </div>
  • CSS
    大局配置
* {
  box-sizing: border-box;
}
body {
  background: #efefbb;
  background: linear-gradient(to right, #d4d3dd, #efefbb);
  margin: 0;
  padding: 0;
  display: flex; /*讓內容在viewport的中間*/
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
h1 {
  letter-spacing: 3px;
}

設定好後呈現如下

接下來要把圖片變成卡片的排版,從縱向變成橫向,呈現大概會如下圖這樣
https://ithelp.ithome.com.tw/upload/images/20221006/20149362ZkzoEjedkU.png

卡片內部

.pokemon {
  background-color: #eee;
  border-radius: 10px;
  box-shadow: 0 3px 15px rgba(100, 100, 100, 0.5);
  margin: 10px;
  padding: 20px;
  text-align: center;
}
.pokemon .img-container {
  background-color: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  width: 120px;
  height: 120px;
  margin: 5px auto;
  text-align: center;
}
.pokemon .img-container img {
  max-width: 90%;
  margin-top: 20px;
}
.pokemon .info .number {
  background-color: rgba(0, 0, 0, 0.1);
  border-radius: 10px;
  padding: 5px 10px;
  font-size: 14px;
}

.pokemon .info .name {
  margin: 15px 0 7px;
  letter-spacing: 1px;
}

樣式的部分大致上是完成了,要來處理今天的核心

  • JS

變數宣告

const container = document.getElementById("container");
const pokemon_count = 150; //要抓的寶可夢的總數
const colors = {
  fire: "#FDDFDF",
  grass: "#DEFDE0",
  electric: "#FCF7DE",
  water: "#DEF3FD",
  ground: "#f4e7da",
  rock: "#d5d5d4",
  fairy: "#fceaff",
  poison: "#98d7a5",
  bug: "#f8d5a3",
  dragon: "#97b3e6",
  psychic: "#eaeda1",
  flying: "#F5F5F5",
  fighting: "#E6E0D4",
  normal: "#F5F5F5",

會設定那麼多顏色,是之後要去為不同寶可夢的類型去做分類,同個類型就同種顏色

API
接下來要從外部的API(點此)取得POKEMON的資料和圖片

可以透過ID、類型、能力、種類來客製化我們的API,在這邊我們選用ID的方式來設計

const fetchPokemons = async () => {
  for (let i = 1; i <= pokemon_count; i++) {
    await getPokemon(i); //這裡i代表id
  }
};
const getPokemon = async (id) => {
  const url = `https://pokeapi.co/api/v2/pokemon/${id}`;
  const res = await fetch(url);
  const data = await res.json();
  
   createPokemonCard(data);
   
     console.log(data);  //請看下圖
};

fetch() 是一個全域的方法,需要傳入一個參數,也就是資料的 URL,會回傳一個包含 response 的 promise,這裡我們用async await的讓語法更簡潔易讀
接著把data印出來,就是一堆密密麻麻的資料啦~okok代表我們成功獲取到資料了
https://ithelp.ithome.com.tw/upload/images/20221006/20149362nKAGKg3mEE.png

接下來我們要回到HTML把部分預先寫好的程式碼先註解掉,因為要直接用JS來操控,HTML只需留下以下這部分即可

  <h1>Pokedex</h1>
    <div class="poke-container" id="poke-container"></div>

建立pokemon元素

const createPokemonCard = (pokemon) => {
  const pokemonEl = document.createElement("div");  //建立元素
  pokemonEl.classList.add("pokemon");  
  
  const pokemonInnerHTML = `
  <div class="img-container">
  <img src="省略不寫" alt="pikachu">
  </div>
<div class="info">
  <span class="number">#001</span>
  <span class="name">Pikachu</span>
  <small class="type"> <span>electric</span> </small>
</div>
    `;
   
  pokemonEl.innerHTML = pokemonInnerHTML;
  container.appendChild(pokemonEl);
  
};

把img-container的內容放到樣板字串中,設定好呈現如下,非常多隻皮卡丘,每張圖都一樣,原因是我在省略的圖片網址中先寫死的,接著要把它成動態呈現,依照不同id代表不同圖片來完成
https://ithelp.ithome.com.tw/upload/images/20221006/201493624qFtCD1cjd.png

動態呈現(編號、名稱、圖片、類型、顏色)

const mainType = Object.keys(colors);

const createPokemonCard = (pokemon) => {
  const pokemonEl = document.createElement("div");
  pokemonEl.classList.add("pokemon");

  //編號
  const id = pokemon.id.toString().padStart(3, "0");  //補0在每個編號前面,共3位數

  //名稱
  const name = pokemon.name[0].toUpperCase() + pokemon.name.slice(1); //第一個字母大寫,其餘都小寫

  //類型
  const pokemonType = pokemon.types.map((type) => type.type.name); 
  const type = mainType.find((type) => pokemonType.indexOf(type) != -1); //不等於-1代表有找到值

  //顏色
  const color = colors[type];
  pokemonEl.style.backgroundColor = color;

  // 圖片
  const pokemonInnerHTML = `
  <div class="img-container">
  <img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png" alt="${name}">
  </div>
  
<div class="info">
  <span class="number">#${id}</span>
  <span class="name">${name}</span>
  <small class="type"> <span>${type}</span> </small>
</div>
    `;
    
  pokemonEl.innerHTML = pokemonInnerHTML;
  container.appendChild(pokemonEl);
};
fetchPokemons();

設定好呈現如下
https://ithelp.ithome.com.tw/upload/images/20221006/2014936208GXQIZkR1.png

以上是這次的project,若有解說不夠詳盡或是錯誤歡迎指教,感激不盡!

附上codepen連結 https://codepen.io/hangineer/pen/zYjJqyE


補充

  1. 什麼是promise

參考資料

50 Projects In 50 Days - HTML, CSS & JavaScript
寶可夢官網


summary 總結 & 完賽感想

本篇用到很多陣列處理方法,像是find()slice()map()indexOf(),還有ferch API ,還會連貫到 promise、async await 的概念,有很多重點中的重點,剛好藉由這個賽project而有再次練習的機會,覺得挺好的~

今天也是最後一篇文了,很開心自己做到了對自己的承諾ヽ(✿゚▽゚)ノ,這30天各種酸甜苦辣,簡單一點的project可以3個小時內完成並打完文章,複雜一點的甚至3-5個小時都有,加上沒有庫存文章(下次參賽要記得庫存!!),所以常常在有時間壓力下趕緊把文章發布出去,過程中最困難的無非就是自律以及把project梳理成文字來呈現其脈絡,參加鐵人賽剛好是一個很好的自我訓練,也謝謝IT邦幫忙提供了這個機會,鐵人賽我們後會有期


上一篇
Day 29 Side Project : Notes App 備忘錄
系列文
在30天利用HTML & CSS & JavaScript完成Side Project實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
112182ssss
iT邦新手 4 級 ‧ 2023-11-17 10:19:12

感謝提供這麼多可以練習的專案☺️☺️☺️

我要留言

立即登入留言