6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IIT(Iwate Industrial Tecnology)Advent Calendar 2024

Day 11

PokeAPIでカード風表示

Last updated at Posted at 2024-12-10

みなさん、「Pokémon Trading Card Game Pocket」(通称:ポケポケ)遊んでいますか?
なんといってもカードを引くのが楽しいですよね。
pokeAPIというものがあったので、そちらでランダムに5匹のポケモンを選定し、
カード風に表示するページを作成してみました。

pokeAPI:https://pokeapi.co/

完成イメージはこちら:
image.png

仕様

「カードを引く」ボタンを押すと、151匹※のうち、ランダムに5匹のポケモンを抽出して表示。
(※ポケモンは151匹だってイマクニ?が言ってました)
そこから、
・名前
・倒したらもらえるポイント(HP)
・タイプ(複数ですが、表示上1つに・・)
・画像
・わざをランダムに2つ(わざのポイント?も取りたかったのですが、力尽きました・・)
pokeAPIからは英語で帰ってくるので、
「名前」と「わざ名」は日本語に変換します。

HTMLはこのような感じです。
「カードを引く」ボタンと、
cardlistという空のulタグだけ用意しています。

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
    <link rel="stylesheet" href="common.css">
</head>

<body>
    <h1>ポケモンカード</h1>
    <a class="button" href="#">カードを引く</a>
    <ul class="cardlist">
        
    </ul>
    <script src="main.js"></script>
</body>

</html>

CSSはSassで記述しました。
タイプをクラスに表示し、カードの背景色とタイプのアイコンと背景色(丸いやつ)をCSSで出し分けしています。
カラーコードは8桁で表記すると透過されるんですね。知らなかった。
タイプ別のカード背景の色分けはなんとなくの色です。
指定カラーコードあるのかな?
タイプアイコンは、fontAwesomeにて、似たようなアイコンを表示させました。
ポケモンの背景は一律に森(byいらすとや)にしました。
こちらもタイプによって出し分けできるといいですね!

common.scss
*,*::before,*::after{box-sizing:border-box}html{-moz-text-size-adjust:none;-webkit-text-size-adjust:none;text-size-adjust:none}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role='list'],ol[role='list']{list-style:none}body{min-height:100vh;line-height:1.5}h1,h2,h3,h4,button,input,label{line-height:1.1}h1,h2,h3,h4{text-wrap:balance}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}

body{
    font-family:"Yu Gothic", "游ゴシック", YuGothic, "游ゴシック体", "ヒラギノ角ゴ Pro W3", "メイリオ", sans-serif;
}
.button{
    background-color: #fff100;
    border-bottom: 5px solid #ccc100;
    text-decoration: none;
    color: #000000;
    padding: 10px 20px;
    position: relative;
    display: block;
    border-radius: 4px;
    font-weight: bold;
    margin: 10px 20px;
    width: 200px;
    text-align: center;
    margin: 20px auto;
}
h1{
    text-align: center;
    margin-top: 40px;
}
.cardlist{
    width: 1200px;
    margin: 0 auto;
    text-align: center;
}
.card{
    display: inline-block;
    border: 10px #cccccc solid;
    border-radius: 10px;
    position: relative;
    width: 300px;
    height: 400px;
    margin-bottom: 20px;
    margin-right: 20px;
    box-sizing: border-box;
    padding: 60px 20px;
    transform-style: preserve-3d;
    transition: all 1s ease-in-out;
    &.normal{ background: #a5a5a5cc;}
    &.fighting{ background: #f89b5dcc;}
    &.flying{ background: #a79ef8cc;}
    &.poison{ background: #8d36ffcc;}
    &.ground{ background: #1a8f0fcc;}
    &.bug{ background: #a0e743cc;}
    &.ghost{ background: #000000cc;}
    &.steel{background: #564bf3cc;}
    &.fire{ background: #f78a31cc;}
    &.water{ background: #33abfacc;}
    &.grass{ background: #2db421cc;}
    &.electric{ background: #f4f73ecc;}
    &.psychic{ background: #f06868cc;}
    &.ice{ background: #37f7f7cc;}
    &.dragon{ background: #1b1296cc;}
    &.dark{background: #4b4a4acc;}
    &.fairy{ background: #f0a7eacc;}
    &:hover {
        transform: rotateY( 180deg );
        cursor: pointer;
      }
}
.card-image{
    border: #000000 solid 1px;
    height: 150px;
    display: flex;
    justify-content: center;
    background: url(img/bg_natural_mori.jpg) center center no-repeat;
    img{
        line-height: 1;
    }
    
}
.card-name{
    position: absolute;
    top: 18px;
    left: 20px;
    -webkit-text-stroke: 1px #fff;
    font-weight: bold;
    font-size: 28px;
    letter-spacing: 0.2;
}
.card-hp{
    position: absolute;
    top: 10px;
    right: 10px;
    font-size: 24px;
    font-weight: bold;
    line-height: 1.8;
    -webkit-text-stroke: 1px #fff;
}
.card-hp span{
    font-size: 14px;
}
.card-hp::after{
	font-family: FontAwesome,"Font Awesome 6 Brands";
    display:inline-block;
    font-size: 12px;
    width: 30px;
    height: 30px;
    background: #e28d8d;
    color: #fff;
    border-radius: 20px;
    text-align: center;
    padding-top: 2px;
    box-sizing: border-box;
    margin-left: 5px;
    vertical-align: 6px;
    border: #000000 solid 2px;
}
.card-hp{
    &.normal::after{ content: "\f111"; background: #a5a5a5;}
    &.fighting::after{content: "\f255"; background: #f89b5d;}
    &.flying::after{content: "\f56b"; background: #a79ef8;}
    &.poison::after{content: "\f714"; background: #8d36ff;}
    &.ground::after{content: "\e52f"; background: #1a8f0f;}
    &.bug::after{content: "\f188"; background: #a0e743;}
    &.ghost::after{content: "\f6e2"; background: #000000;}
    &.steel::after{content: "\f0ad"; background: #564bf3;}
    &.fire::after{content: "\f7e4"; background: #f78a31;}
    &.water::after{content: "\f043"; background: #33abfa;}
    &.grass::after{content: "\f299"; background: #2db421;}
    &.electric::after{content: "\f0e7"; background: #f4f73e;}
    &.psychic::after{content: "\f8d9"; background: #f06868;}
    &.ice::after{content: "\f2dc"; background: #37f7f7;}
    &.dragon::after{content: "\f6d5"; background: #1b1296;}
    &.dark::after{content: "\f186"; background: #4b4a4a;}
    &.fairy::after{content: "\f69a"; background: #f0a7ea;}
}
.card-move{
    -webkit-text-stroke: 0.5px #fff;
    margin-top: 40px;
    text-align: left;
    font-size: 24px;
    font-weight: bold;
}

さて、Javascriptです。
日本語の取得する際。別のAPIから取得する必要があるのですが、
表示の関数(displayPokemon)が先に走ってしまい苦労しました。
こちらは、asyncを使用することで無事に日本語名を取得することができました。
・ポケモン5匹の情報取得
・ポケモン名の日本語表記
・わざの日本語表記×2
と、4回もAPIをfetchしております。

main.js
// カードリストのDOM
const cardlist = document.querySelector(".cardlist");

// ボタンのDOM
const button = document.querySelector(".button");

// 「カードを引く」ボタンを押したら表示
button.addEventListener("click",function(){
    getPokemon();
});

// ポケモンのAPI取得
async function getPokemon(){
   const promises = [];

//    1から151までの5つのランダムな数値を配列に代入
   const randomNumbers = Array.from({ length: 5 }, () => Math.floor(Math.random() * 151) + 1);
   
    // ポケモンのAPIランダムに5つ取得
   for(const num of randomNumbers) {
    const url = `https:pokeapi.co/api/v2/pokemon/${num}`;
    // Promiseの配列に結果を代入
    promises.push(await fetch(url)
    .then(response => {
        if (!response.ok) {
            throw new Error('ネットワークエラーが発生しました');
        }
        return response.json();
    }))
};

   Promise.all(promises).then( results => {
       const pokemon = results.map((data) => ({
           name: data.name,
           id: data.id,
           image: data.sprites.other['official-artwork'].front_default,
           ex: data.base_experience,
           type: data.types.map((type) => type.type.name).join(' '),
           moves: data.moves.map((move) => move.move)
       }));
    
    //    日本語名取得
       japaneseName(pokemon);
   });
};

// ポケモンの表示
async function displayPokemon(pokemon){
    console.log(pokemon);
    let pokemonHTNLString = "";
    for( poke of pokemon ){
        pokemonHTNLString +=
        `<li class="card  ${poke.type}">
        <h2 class="card-name">${poke.name}</h2>
        <p class="card-hp ${poke.type}"><span>HP</span> ${poke.ex}</p>
        <div class="card-image"><img src="${poke.image}"/></div>
        <p class="card-move">${poke.moves} </p>
        </li>  `
    }
   cardlist.innerHTML = pokemonHTNLString;
};

// 日本語名の取得
async function japaneseName(pokemon){
    for( poke of pokemon ){
        // 日本語名
        const nameurl = `https://pokeapi.co/api/v2/pokemon-species/${poke.name}`;
        await fetch(nameurl)
        .then(response => {
            if (!response.ok) {
                throw new Error('ネットワークエラーが発生しました');
            }
            return response.json();
        }).then(results => {
            for(info of results["names"]){
                if(info["language"]["name"] == 'ja-Hrkt'){
                    poke.name = info['name']; 
                    break;
                }
            }
        });

        // 技の日本語名
        // 2つのランダムな数値を配列に代入
        const randMoves = Array.from({ length: 2 }, () => Math.floor(Math.random() * poke.moves.length) + 1);
        poke.moves = "";
        for(i in randMoves){
                const moveurl = `https://pokeapi.co/api/v2/move/${randMoves[i]}`;
                await fetch(moveurl)
                .then(response => {
                    if (!response.ok) {
                        throw new Error('ネットワークエラーが発生しました');
                    }
                    return response.json();
                }).then(results => {
                    for(info of results["names"]){
                        if(info["language"]["name"] == 'ja-Hrkt'){
                            poke.moves += info['name'] + "<br>"; 
                            break;
                        }
                    }
                });
        }
    }
    // カードを表示
    await displayPokemon(pokemon);
}

完成!
なんだかかわいい~

Document-Google-Chrome-2024-12-06-14-16-23.gif

APIを合計4回も呼び出しているので、表示に時間がかかっています。。
つぎは「わざ」の情報を充実させたいです。

参考サイト:
JavaScript -ポケモン一覧デモサイトを作ってみた-
https://note.com/branch_it_sol205/n/na6fc2449a4bb
PokeAPI×Reactで作成したポケモン図鑑を日本語表記にする方法
https://zenn.dev/h_aso/articles/2220c857f5b74c
pokeAPIからポケモンのデータを取得する
https://zenn.dev/wanderer_eel/articles/ebcbdd0b034e94

6
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?