【チーム開発】SP対応とネストするSwiper
投稿日: 2024年11月22日
もはやチーム開発関係ないのですが、一連の案件なので一応タイトルにつけてます。
SP対応のデザインは着手してから1週間程度遅れてきたのですが、私の範囲はどんな感じかな~とみていると。。。
PCと全然デザイン違うやん。これ含めて2週間?できるんかいな??
デザインだけ見てもわからんこと多すぎるから確認しよう!って思いました。
PC終わってSP取り掛かる段階で質問しました!
やった後に勘違いだったってやり直すのは時間の無駄なので避けたくて、先にこういうことですかっていうのは全部投げかけました!!
丁寧に回答いただけたので理解してから着手しました。
基本は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番最初に表示して、そこからすべて表示するという要件付きなので、少し処理は必要です。
direction="vertical"
こちらが縦スワイプであることを示していて、
direction="horizontal"
こちらは横スワイプです。
横スワイプであることは明示しなければデフォルトが横なので大丈夫なんですけど、今回は縦も横もあるのであえてこちらは横スワイプですよと伝えようと思い書きました。
そんなに特別なことはしていませんが
loop={2 <= item.articleFiles.length}
これは最後まで表示された際にループするかどうかのbooleanですが、件数によってなのでloop
しか指定しないと(全ての場合でtrue)、1件しかデータがない時に警告かエラーか記憶失いましたがコンソールに出てました。
ですので条件付きにしています。
最初できるかなと構えたんですけどハマる点もなく普通にできました。
今回全体的にSwiperの実装が多い案件だったのように思うのですが、もしかしてアニメーションが多いってこのことも含むんですかね?
CSSのアニメーションよくわからんけどSwiperならLPのカルーセルでも使い、オリアプでも使い、今回も使ったし使える・・ようになってると思う・・多分・・って感じなんですが、、、
figmaのコメントにもイメージとして「インスタ」ってワードがあったので、インスタ作れないよ私みたいな感じにおもったんですけど、(パッと思っただけです。深く考えてないので、ほんとに無理って落ち込んだりは一切ないです。できんやろーやるか。って感じなのでいつも笑)、やればできるものですね。
絶対やり切るのは大前提ですが、「わからんけど、まぁやればできるんかな?」くらいの気持ちでいつも取り掛かるようにしています。
実際公開されたら操作しにいくのが楽しみです!!