みなさん、「Pokémon Trading Card Game Pocket」(通称:ポケポケ)遊んでいますか?
なんといってもカードを引くのが楽しいですよね。
pokeAPIというものがあったので、そちらでランダムに5匹のポケモンを選定し、
カード風に表示するページを作成してみました。
pokeAPI:https://pokeapi.co/
仕様
「カードを引く」ボタンを押すと、151匹※のうち、ランダムに5匹のポケモンを抽出して表示。
(※ポケモンは151匹だってイマクニ?が言ってました)
そこから、
・名前
・倒したらもらえるポイント(HP)
・タイプ(複数ですが、表示上1つに・・)
・画像
・わざをランダムに2つ(わざのポイント?も取りたかったのですが、力尽きました・・)
pokeAPIからは英語で帰ってくるので、
「名前」と「わざ名」は日本語に変換します。
HTMLはこのような感じです。
「カードを引く」ボタンと、
cardlistという空のulタグだけ用意しています。
<!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いらすとや)にしました。
こちらもタイプによって出し分けできるといいですね!
*,*::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しております。
// カードリストの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);
}
完成!
なんだかかわいい~
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