【チーム開発】SP対応とネストするSwiper

【チーム開発】SP対応とネストするSwiper

投稿日: 2024年11月22日

学習振り返り
要約
  • SP対応のデザインが遅れてきたが、PCデザインとの違いを確認しながら進めた。
  • デバイスに応じた表示をフックで分け、Swiperを利用してネストされたスワイプ機能を実装した。
  • 最初は不安だったが、やればできるという感覚を持って取り組んだ結果、特に問題なく実装できた。

はじめに

もはやチーム開発関係ないのですが、一連の案件なので一応タイトルにつけてます。

SP対応のデザインは着手してから1週間程度遅れてきたのですが、私の範囲はどんな感じかな~とみていると。。。

【チーム開発】インスタみたいな縦スワイプ|ShiftBブログ

初見の感想

PCと全然デザイン違うやん。これ含めて2週間?できるんかいな??

デザインだけ見てもわからんこと多すぎるから確認しよう!って思いました。

PC終わってSP取り掛かる段階で質問しました!

【チーム開発】インスタみたいな縦スワイプ|ShiftBブログ

やった後に勘違いだったってやり直すのは時間の無駄なので避けたくて、先にこういうことですかっていうのは全部投げかけました!!

丁寧に回答いただけたので理解してから着手しました。

方針

基本はCSSで実装してくださいと言われてました。

ですがしかし!!

画像一覧ページ

PCはホバーだけどSPはクリックでモーダル開く処理が必要

投稿ページ

PCはスワイプするの写真だけで、SPは投稿ごと縦スワイプ(縦スワイプの中に横スワイプ→ネストする・・?)

このあたりはCSSで対応厳しいと思ったので、フックでデバイス判断して出しわける形でやろうとデザインみて決めました。

ちなみに、ポータルアプリのSP対応はすべてフックで実装してます!

なので、PCで起動して画面幅狭めたらPCのままで、リロードしたらSPモードになりますよ!

イメージ

import { useDevice } from "@/app/_hooks/useDevice";
  
export const hoge: React.FC = () => {
 const { isSp } = useDevice();

 if(isSp){
   return <p>SPの場合の画面を返す</p>
 }

 return <p>PCの場合の画面を返す</p>

}

こんな感じでデバイスに応じて返す内容を変える形です!

共通のものが少ない場合がつかい時かなと思います!!

モーダルはコンポーネントごとPCとSPで分けちゃいました。

本題

縦スワイプ(外)と横スワイプ(中)のネスト

意外とシンプルでほんとにただネストするだけです。

<Swiper> 
  {/**投稿データここからループ*/}
  <SwiperSlide>
    <Swiper> 
      {/**写真データここからループ*/}
      <SwiperSlide>
      
      </SwiperSlide>
    </Swiper>
  </SwiperSlide>
</Swiper> 

こんなかんじで、特別なオプション持たせたりも必要ないです。

実際のコード

import { Pagination } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/pagination";
import { ArticleContents } from "./ArticleContents";
import { FullScreenModal } from "@/app/_components/FullScreenModal";
import { MediaRenderer } from "@/app/_components/MediaRenderer";
import { ArticleData } from "@/app/_types/ArticleFiles";
import { Article } from "@/app/_types/Articles";

interface Props {
  articleId: number;
  files: ArticleData[];
  fileId: number;
  isOpen: boolean;
  onClose: () => void;
  articles: Article[];
}

export const ArticleSpModal: React.FC<Props> = ({
  articles,
  files,
  fileId,
  isOpen,
  onClose,
  articleId,
}) => {
  // クリックされた投稿を最初に配置
  const targetIndex = articles.findIndex(item => item.id === articleId);
  const orderedArticles = [
    articles[targetIndex],
    ...articles.slice(0, targetIndex),
    ...articles.slice(targetIndex + 1),
  ];
  // クリックされた画像を最初に配置
  const targetFileIndex = files.findIndex(item => item.id === fileId);
  const orderedFiles = [
    files[targetFileIndex],
    ...files.slice(0, targetFileIndex),
    ...files.slice(targetFileIndex + 1),
  ];
  orderedArticles[0].articleFiles = orderedFiles;
  return (
    <FullScreenModal isOpen={isOpen} onClose={onClose}>
      <div className="h-full">
        <Swiper
          direction="vertical"
          slidesPerView={1}
          className="max-h-dvh w-full p-2"
          initialSlide={0}
          nested
        >
          {orderedArticles.map(item => (
            <SwiperSlide key={item.id} className=" overflow-y-auto">
              <Swiper
                slidesPerView={1}
                centeredSlides
                direction="horizontal"
                pagination={true}
                modules={[Pagination]}
                className="w-full px-8 [&_.swiper-pagination]:relative [&_.swiper-pagination]:!top-1"
                loop={2 <= item.articleFiles.length}
                initialSlide={0}
              >
                {item.articleFiles.map(file => (
                  <SwiperSlide key={file.id} className="">
                    <div className="relative h-[457px] w-full">
                      <MediaRenderer imageUrl={file.imageUrl} autoPlay />
                    </div>
                  </SwiperSlide>
                ))}
              </Swiper>
              <div className="">
                <ArticleContents article={item} modal />
              </div>
            </SwiperSlide>
          ))}
        </Swiper>
      </div>
    </FullScreenModal>
  );
};

クリックされた投稿、クリックされた画像を1番最初に表示して、そこからすべて表示するという要件付きなので、少し処理は必要です。

Swiperに渡すオプション

direction="vertical"

こちらが縦スワイプであることを示していて、

direction="horizontal"

こちらは横スワイプです。

横スワイプであることは明示しなければデフォルトが横なので大丈夫なんですけど、今回は縦も横もあるのであえてこちらは横スワイプですよと伝えようと思い書きました。

そんなに特別なことはしていませんが

loop={2 <= item.articleFiles.length}

これは最後まで表示された際にループするかどうかのbooleanですが、件数によってなのでloopしか指定しないと(全ての場合でtrue)、1件しかデータがない時に警告かエラーか記憶失いましたがコンソールに出てました。

ですので条件付きにしています。

おわりに

最初できるかなと構えたんですけどハマる点もなく普通にできました。

今回全体的にSwiperの実装が多い案件だったのように思うのですが、もしかしてアニメーションが多いってこのことも含むんですかね?

CSSのアニメーションよくわからんけどSwiperならLPのカルーセルでも使い、オリアプでも使い、今回も使ったし使える・・ようになってると思う・・多分・・って感じなんですが、、、

figmaのコメントにもイメージとして「インスタ」ってワードがあったので、インスタ作れないよ私みたいな感じにおもったんですけど、(パッと思っただけです。深く考えてないので、ほんとに無理って落ち込んだりは一切ないです。できんやろーやるか。って感じなのでいつも笑)、やればできるものですね。

絶対やり切るのは大前提ですが、「わからんけど、まぁやればできるんかな?」くらいの気持ちでいつも取り掛かるようにしています。

実際公開されたら操作しにいくのが楽しみです!!

シェア!

Threads
user
吉本茜
山口在住/二児の母
Loading...
記事一覧に戻る
Threads
0