🔖

ポケポケ(ポケモンカード)のアニメーションを実装しました

2025/03/03に公開

はじめに

ADHDエンジニアの橋田至です。

https://x.com/dall_develop

今回は以前に実装したポケモンカードの続きになります。

https://zenn.dev/dirtyman/articles/d8f920ce595e95

この記事はありがたいことにたくさんの方に見ていただけました。

やはりポケモンというドメインはみんな大好きで、興味が高く、需要が高いのだなと実感しました。

以前はポケモンカードのUIの実装のみでしたが、今回は更にホバー時の3D アニメーションを実装したので、その実装方法を解説します。

ちなみにポケモンカード自体の実装方法やこの記事を公開した理由などは以前の記事に詳しく記載しているため、そちらもご覧になっていただければ幸いです。

実装の詳細

少し分かりづらいかもしれませんが、ホバー時に少し角度が変わるようになっているのがお分かりでしょうか?

ポケモンカードがホバーされたときに、カードが回転しているように見せるために、CSSのtransformプロパティとReactのイベント処理を組み合わせて実装しました。

実装したサイト

https://my-dq-portfolio.vercel.app/portfolio

実装方法

まず、ホバー時にカードが回転するアニメーションを作成するために、ReactのuseStateフックを使用してカードの回転角度を管理します。また、マウスの位置に基づいてカードが回転するように、onMouseMoveイベントを使います。

具体的には、次のような流れで実装を進めます:

  • マウスの位置を取得: onMouseMoveイベントで、マウスの位置をカードの中心と比較して、カードの回転角度を計算します。
  • 回転角度の設定: 計算した回転角度をtransformスタイルに反映させ、リアルタイムでカードを回転させます。
  • カードのアニメーション: CSSでアニメーションを適用し、滑らかに回転させます。

コードの詳細

以下のコードでは、Reactを使ってポケモンカードを3Dで回転させる実装を示しています。handleMouseMoveでマウスの位置を取得し、それに基づいてrotateXとrotateYの値を更新しています。

import { useState } from 'react';

type PokemonCardProps = {
  back: string;
  description: string;
  id: string;
  img: string;
  name: string;
};

const PokemonCard = ({ card }: { card: PokemonCardProps }) => {
  const [isHovered, setIsHovered] = useState(false);
  const [rotateX, setRotateX] = useState(0);
  const [rotateY, setRotateY] = useState(0);

  const handleMouseEnter = () => setIsHovered(true);
  const handleMouseLeave = () => setIsHovered(false);

  const handleMouseMove = (e: React.MouseEvent) => {
    const cardRect = e.currentTarget.getBoundingClientRect();
    const centerX = cardRect.left + cardRect.width / 2;
    const centerY = cardRect.top + cardRect.height / 2;

    // マウスの位置を取得
    const deltaX = e.clientX - centerX;
    const deltaY = e.clientY - centerY;

    // 回転の値を計算
    const rotateXValue = (deltaY / cardRect.height) * 20; // Y軸に基づく回転
    const rotateYValue = (deltaX / cardRect.width) * -20; // X軸に基づく回転

    setRotateX(rotateXValue);
    setRotateY(rotateYValue);
  };

  return (
    <div
      className={`relative w-72 h-96 border rounded-lg shadow-lg overflow-hidden transform transition-transform duration-300 ${isHovered ? 'scale-105' : ''}`}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      onMouseMove={handleMouseMove}
      style={{
        transform: `rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.05)`, // rotateとscaleを一つのtransformで適用
        transformOrigin: 'center center', // 回転の基準をカードの中心に設定
      }}
    >
      {/* カードの表面 */}
      <div className="absolute inset-0 bg-black">
        <img
          alt={card.name}
          className="w-full h-full object-cover"
          src={card.img}
        />
      </div>
      {/* カードの裏面 */}
      <div
        className={`absolute inset-0 bg-gradient-to-br from-gray-800 to-gray-600 flex items-center justify-center ${isHovered ? 'opacity-0' : 'opacity-100'} transition-opacity duration-300`}
      >
        <img alt="Pokémon Card Back" className="w-2/3 h-auto" src={card.back} />
      </div>
      {/* カード情報 */}
      {isHovered && (
        <div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-75 text-white p-4">
          <h2 className="text-lg font-bold">{card.name}</h2>
          <p className="text-sm">{card.description}</p>
        </div>
      )}
    </div>
  );
};

export default PokemonCard;

ポイント:

  • handleMouseMove関数でマウスの動きに応じてrotateXとrotateYを更新し、リアルタイムでカードの回転を制御しています。
  • CSSでtransform-origin: 'center center'を設定し、回転の中心をカードの中央に指定しています。
  • transformをrotateXとrotateYの値に基づいて動的に変更し、カードが滑らかに回転するようにしています。

該当のcommit

https://github.com/developerhost/my-dq-portfolio/commit/730a6bd1b84797d316b80d756b126c1ec31315b1

改善点

今後はよりリッチにカードを動かしたり、エフェクトも出せるようにしていきたいと考えています。

最後に

より良い実装方法などがございましたら気軽にIssue起票、PR作成お願いいたします

https://github.com/developerhost/my-dq-portfolio

また、この記事が良い内容だと思ったらいいねをいただけるとすごく喜びます。

橋田至より。

Discussion