1
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?

はじめての記事投稿
Qiita Engineer Festa20242024年7月17日まで開催中!

モジュロ演算の魔法: Reactを使った画像切り替えの実装での応用方法

Posted at

モジュロ演算とは

簡単にいうと、割り算の余り(モジュロ)を求める計算です。例えば、

7 % 3 = 1 // 2余り1

2 % 4 = 1 // 0余り2

この余りの部分を求める計算になります。

インデックスを計算する

ウェブアプリケーションで画像切り替えなどのコンポーネントを実装する場合、前後のアイテムへのナビゲーションはインデックスの計算にモジュロ演算が使われます。以下はReactを使用したサンプルコードです。

今回作ったもの

Slide-App.gif

使用技術

  • React 18.2.0
  • TypeScript
  • Tailwind CSS
  • Heroicons
  • Vite
App.tsx
import { useState } from 'react';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/16/solid';

interface Props {
  id: number;
  title: string;
  thumbnail: string;
}

const slides: Props[] = [
  {
    id: 1,
    title: "赤いサムネイル",
    thumbnail: "/thumb1.png"
  },
  {
    id: 2,
    title: "緑のサムネイル",
    thumbnail: "/thumb2.png"
  },
  {
    id: 3,
    title: "青いサムネイル",
    thumbnail: "/thumb3.png"
  },
]

function App() {
  const [currentIndex, setCurrentIndex] = useState(0);

  const nextSlide = () => {
    const nextIndex = (currentIndex + 1) % slides.length;
    setCurrentIndex(nextIndex);
  }

  const prevSlide = () => {
    const prevIndex = (currentIndex - 1 + slides.length) % slides.length;
    setCurrentIndex(prevIndex);
  }

  const currentSlide = slides[currentIndex];

  return (
    <>
      <div className="flex justify-center items-center min-h-screen">
        <div className="w-[500px] rounded-lg bg-white shadow-md py-6 px-8 relative">
          <div>
            <div className="mb-4">
              <img src={currentSlide.thumbnail} alt={currentSlide.title} />
            </div>
            <p>{currentSlide.title}</p>
          </div>
          <button
            type="button"
            className="absolute top-1/2 -left-5 flex justify-center items-center rounded-full w-10 h-10 border border-zinc-500 -translate-y-1/2 bg-white"
            onClick={prevSlide}
          >
            <span className="sr-only">前のスライド</span>
            <ArrowLeftIcon className="size-6 text-zinc-700" />
          </button>
          <button
            type="button"
            className="absolute top-1/2 -right-5 flex justify-center items-center rounded-full w-10 h-10 border border-zinc-500 -translate-y-1/2 bg-white"
            onClick={nextSlide}
          >
            <span className="sr-only">次のスライド</span>
            <ArrowRightIcon className="size-6 text-zinc-700" />
          </button>
        </div>
      </div>
    </>
  )
}

export default App

モジュロ演算は次のように使われています。

const nextIndex = (currentIndex + 1) % slides.length;
const prevIndex = (currentIndex - 1 + slides.length) % slides.length;

次のサムネイルのインデックスを計算する

nextIndexですが、次のサムネイルに移るときインデックスを1つ増やします。初回のサムネイルがインデックス0なので、次のサムネイルは1、その次のサムネイルは2となります。

しかし、現在のサムネイルが最後のサムネイル(インデックス2)だとすると、次のサムネイルは最初のサムネイル(インデックス0)に戻る必要があります。このため、モジュロ演算を使用します。

const nextIndex = (currentIndex + 1) % slides.length;

例えば、現在のインデックスが2(最後のサムネイル)であれば、

(2 + 1) % 3 = 3 % 3 = 0

これで最初のサムネイルに戻ります。

前のサムネイルのインデックスを計算する

prevIndexですが、前のサムネイルに移るときインデックスを1つ減らします。例えば、現在のサムネイルのインデックスが2だとすると、前はインデックス1、その前のサムネイルは0になります。

しかし、現在のサムネイルが最初のサムネイル(インデックス0)だとすると、次のサムネイルは最後のサムネイル(インデックス2)に戻る必要があります。このため、モジュロ演算を使用します。

const prevIndex = (currentIndex - 1 + slides.length) % slides.length;

例えば、現在のインデックスが0(最初のサムネイル)であれば、

(0 - 1 + 3) % 3 = 2 % 3 = 2

これで最後のサムネイルに戻ります。

まとめ

インデックスを計算しなければならない場面は結構あるのかなと感じます。(例えば音楽プレイヤーの次の曲、前の曲の実装)

自分はドがつく文系出身ですので理解に苦労しました。自分のように計算が苦手な方に届いたらいいなと思います。

何か他に良い実装方法があれば教えていただければ幸いです。

参考

1
1
0

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
1
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?