🏄‍♂️

【Next.js】ANGEL Dojoでフロント実装に使用したライブラリ

2024/03/24に公開

昨年参加させていただいたANGEL Dojoに関する記事です。
私がフロント担当だったかつNext.js(Page Router)を使用したため、そちらで使用したライブラリを一部紹介させていただく内容になります。

この記事を通して、「へー、こんなのあるんだ」と思っていただけると幸いです。

ANGEL Dojoとは

ANGEL Dojoは、AWS(Amazon Web Service)が主催する若手エンジニア向けのトレーニングイベントになります。
基本2社1組のチームで、AWS関連の座学・サービス開発を進めていく3ヶ月ほどのプログラムです。
https://aws.amazon.com/jp/blogs/psa/2023-11-angel-dojo/

作ったもの

私のチームでは、「Chacca」というスマホ向けの自己啓発系?webアプリを開発しました。
これは、やるべきことがあるけどだらけてしまう人向けに行動の動機を強くさせるサービスです。

ユーザーがタスク作成時に金額と期限を設定し、その期限までに開始(着火)されなければ設定した金額がアプリに課金されます。開始(着火)されれば、お祝いのアニメーションが表示されます。

惜しくも賞をいただくことはできなかったですが、チーム開発の進め方・AWSの一般的なサービスの使い方など重要なことを学びました。運営の皆様ありがとうございました。

使用したライブラリ

では使用したライブラリの紹介になります。

NextUI

メインのUIライブラリは、NextUIを使用しました。これはNext.js向けのUIライブラリです。
https://nextui.org/

選定理由としては、Next.jsと相性が良いと聞いたことと、純粋に興味があったためです。

Chaccaでは、UI実装のほとんどにこちらを使用しています。また、NextUI自体がTailwindCSSを使用されているため、簡単なセットアップを行えばTailwindCSSも使えるので、デザインの調整がやりやすいです。

サンプルコードとして、失敗通知モーダル(作ったものの真ん中の画面)は以下になります。

import {
  Button,
  Modal,
  ModalBody,
  ModalContent,
  ModalFooter,
  ModalHeader,
  useDisclosure,
} from '@nextui-org/react';

export const NotifyFailedModal: FC<{
  task: Task;
}> = ({ task }) => {
  const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure({
    defaultOpen: true,
  });
  return (
    <Modal
      //モーダルの外押下した際に、モーダルが閉じないようにする
      isDismissable={false}
      //初回描画時に開く
      defaultOpen
      //表示状態(Boolean)
      isOpen={isOpen}
      //開閉時に実行する
      onOpenChange={onOpenChange}
      //画面中央に表示する
      placement="center"
    >
      <ModalContent>
        {(onClose) => (
          <>
            <ModalHeader className="flex flex-col gap-1">
              タスク開始失敗
            </ModalHeader>
            <ModalBody>
              <p>
                <span className="font-bold">{task.name}</span>{' '}
                の開始期限までに、開始されなかったため失敗しました。
              </p>
              <p>
                本日、開始失敗タスクの累計金額をクレジットカード宛に請求いたします。
              </p>
            </ModalBody>
            <ModalFooter>
              <Button
                //ローディング状態(Boolean)
                isLoading={isLoading}
                onClick={onSubmit}
                color="warning"
                type="submit"
              >
                確認しました
              </Button>
            </ModalFooter>
          </>
        )}
      </ModalContent>
    </Modal>
  );
};

こちらでは大きくModalとButtonのみを扱っていますが、Componentの粒度やPropsの扱いは他のUIライブラリと大きく異なるようなものはなく、他のUIライブラリの経験があればキャッチアップがしやすいと感じます。また、useDisclosureというモーダルの状態管理に適したHooksがNextUIから提供されているため、管理が楽です。

ただ、提供しているUIコンポーネントの数がMUI、Mantineなどに比べると結構少ないので、今回のように小規模で開発期間が短い場合には有用でしたが、そうでない開発の場合に現状あまり向いていないのかなと感じました。

Lottie for react

アニメーションにはLottieを使いました。Lottie for ReactはReact向けのLottieです。
https://lottiereact.com/

Lottie自体は、Adobeで作成したアニメーションをJSON形式で書き出して、それをアプリに組み込んで表示することができる技術です。Lottie For ReactはそのJSONをアプリに組み込む部分が、Reactの書き方に特化しているものになります。

chaccaでは成功画面のアニメーション表示(作ったものの右の画面)にこちらを使いました。
使用したアニメーションのJSONファイルは公式から提供されている以下2つです。
https://lottiefiles.com/animations/fire-NG4n3YU51z
https://lottiefiles.com/animations/high-flame-lSzwML6q0b

以下はそちらを描画したサンプルコードです。

import Lottie from 'lottie-react';
import LottieFireMainAnimation from './lottie-fire-main.json';
import LottieFireSideAnimation from './lottie-fire-side.json';
import { useViewportSize } from '@mantine/hooks';

export const FireAnimation: FC<Props> = () => {
  const window = useViewportSize();
  return (
    <div>
      {/* 左側にある炎 */}
      <Lottie
        //JSONファイルの設定
        animationData={LottieFireSideAnimation}
        //ループする
        loop={true}
        style={{
          //横幅を、画面の高さ分だけ大きくする
          width: window.height,
          //90度回転させ、縦向きにする
          transform: 'rotate(90deg)',
          //左上起点に表示する 
          position: 'absolute',
          top: 0,
          left: 0,
        }}
      />
      {/* 右側にある炎 */}
      <Lottie
        animationData={LottieFireSideAnimation}
        loop={true}
        style={{
          width: window.height,
          transform: 'rotate(270deg)',
          position: 'absolute',
          //右上起点に表示する
          top: 0,
          right: 0,
        }}
      />
      {/* 中央にある炎 */}
      <Lottie
        animationData={LottieFireMainAnimation}
        loop={true}
        style={{
          //サイズ指定
          width: 300,
          height: 300,
          //画面中央寄せ
          position: 'absolute',
          top: '50%',
          left: '50%',
          transform: 'translate(-50%, -80%)',
        }}
      />
    </div>
  );
};

こちらは開発者のコストが少なく、リッチなアニメーションを描画できるのでとても有用でした。

今回、「アニメーション描画はどう実装しよう?」という話になった際に、CSSで複雑なスタイルを記述するかCanvasを扱えるライブラリなど用いるかと言う案が出ていて、どちらも結構時間がかかるものでした。ですが、このLottie For ReactではJSONファイルをこちらで作成していないこともあり、とても簡単にアニメーション描画ができました。これからもあらゆる場面で使っていきたいです。

Swiper

画面のスライダーにはSwiperを使いました。こちらもLottie同様に、React用のライブラリを使用しました。
https://swiperjs.com/react

Swiperはスライダーが作れるJavaScriptライブラリで、Webアプリ・ネイティブアプリで使えるようです。

Chaccaでは、HOME画面(作ったものの左画面)で左にスライドするともう一つ画面が表示されます。こちらは、期限が近いタスクを表示する画面になっていて、ユーザーの焦燥感をより沸き立たせる意図があります。

こちらのサンプルコードとしては以下のようになっています。

import 'swiper/css';

import { CountDownTemplate } from '@/features/countDown/templates';
import { MainTemplate } from '@/features/main/template';
import DefaultLayout from '@/layouts/default';
import { Swiper, SwiperSlide } from 'swiper/react';

export default function IndexPage() {
  return (
    <>
      <Swiper className="mySwiper h-full">
        <SwiperSlide>
          {/* カウントダウンの画面 */}
          <CountDownTemplate />
        </SwiperSlide>
        <SwiperSlide>
          {/* タスク一覧の画面 */}
          <DefaultLayout tab="user">
            <MainTemplate />
          </DefaultLayout>
        </SwiperSlide>
      </Swiper>
    </>
  );
}

           

こちらはとても簡単にスライダー実装できるのでとても有用でした。Swiper用の特別な設定があまりないかつ、React向けのライブラリでは宣言的な記述になるため、コードが汚くなりずらいのもとてもありがたいです。
今回は使用していないですが、useSwiperなどのHooksが提供されているのも魅力的です。
https://swiperjs.com/react#useswiper

またこれは最近知ったのですが、スライダー系のライブラリでSlickというものもあり、こちらもReact向けのライブラリを提供しているようです。こちらも触ってみたいですね。
https://react-slick.neostack.com/

NCDCエンジニアブログ

Discussion