Inside cmn!

山本 慎二

swup.jsでモーダルウィンドウ風UIを簡単に実装する方法

UI/UXプログラミングTips

この記事はどんな人向け?

  • swup.jsって何?という人
  • UX/UI的にモーダルウィンドウを使いたいけど独自のURLも付与したい人
  • UX/UI的にモーダルウィンドウを使いたいけどブラウザデフォルトの操作感を維持したい人

はじめに

こんにちは、フロントエンドエンジニアの山本です👋

swup.jsは簡単に非同期遷移(SPAのようなスムーズなページ遷移)を実装できるJSライブラリです。

swup.jsでの画面遷移のデモ

今回は、このswup.jsで実装できるモーダルウィンドウ風UIの実装方法について説明したいと思います。

swup.jsとは

まず、swup.jsとは何かというと、冒頭で書いた通り非常に簡単に非同期遷移を実装できるJavascriptライブラリで、他のjQuery、React、Vueなどのフレームワークに依存せず利用できます。

小規模〜中規模のプロジェクトでReactやVueまでは不要なものの非同期遷移でリッチな印象を与えたいというニーズにハマるライブラリで、当社でも最近頻繁に利用しています。

また、プラグインという仕組みを使うことでさらに細かなニーズに対応することも可能で、今回はその中の「Fragment Plugin」を使用するモーダルウィンドウ風UIの実装方法について説明します。

基本の遷移(フェード)

応用編の前に、一番簡単な基本の遷移の実装ですが、これは本当にシンプルで

  1. CDNからswup.jsを読み込む
  2. 遷移時に中身のコンテンツが差し代わる要素に特定のIDとクラスを付与
  3. 遷移時のアニメーション(transition)をCSSで指定
  4. swup.jsを初期化

とたったの4ステップです!

まずは、CDNからJSを読み込みます(CSSは不要)

<script src="https://unpkg.com/swup@4"></script>

次に遷移時に中身が差し代わるコンテナーにswupというIDとtransition-fadeというクラスを付与します。

<main id="swup" class="transition-fade">
  <!-- 差し代わるコンテンツが入ります -->
</main>

続いて、アニメーションをCSSで指定します。サンプルのコードはシンプルなフェードイン/アウトです。

※標準ではCSSのtraisitionに際して発火するtransitionendなどのイベントをトリガーにコンテンツの差し替え処理を実行するので、これを忘れると動きません。

html.is-changing .transition-fade {
  transition: opacity 250ms ease;
  opacity: 1;
}

html.is-animating .transition-fade {
  opacity: 0;
}

最後にJSでswup.jsを初期化します。

// swup初期化
const swup = new Swup();

tada! 🎉
基本はこれだけ、簡単ですよね!

モーダルウィンドウ風UI(with Fragment Plugin)

モーダルウィンドウ風UIの実装では画面上のさらに特定の部分(fragment)だけのコンテンツの差し替えを実行するために、前述したFragment pluginを利用します。

swup.jsを使ったモーダルの実装ではURLが変化するため、開いた詳細画面をSNS等で共有可能にしたいケースなどで有用で、ブラウザの標準操作が生きるので自然に操作できるページになり、UXの向上が見込まれます。また、ページ数が増え、検索エンジンに通常ページとしてクロール・インデックスされるのでSEO対策にも効果が見込めるかと思われます。

Fragment pluginは、今回取り扱うモーダルのようなページ遷移の他、フィルターを使ったコンテンツの切り替えや絞り込み時に、一覧部分の表示を切り替えるというようなケースでも利用できます。

利用方法は、簡単にまとめると以下の4ステップです。

  1. CDNでプラグインを読み込む
  2. モーダルが開いた後のページを実装(この時、背景になるコンテンツもそのまま存在するようにする)
  3. モーダルが開く前のページにプレースホルダーとしてtemplate要素を追加
  4. swup.jsにオプションを指定

まずは基本で読み込んだコアのJSファイルの後にプラグインを読み込みます

<script src="https://unpkg.com/swup@4"></script>
<script src="https://unpkg.com/@swup/fragment-plugin@1"></script><!-- プラグインファイル -->

次に「モーダルが開いた後のページを実装」ですが、モーダルウィンドウの開閉に関するJSの実装などは無視して構わないので、モーダルを開いた後のUI(公式ではdialog要素を使うことを推奨)を実装します。
この時、Fragment Pluginを使った遷移では対象部分(モーダル・リストなど)のみを再描画するため、背景になるコンテンツも遷移後のページに必要なのでその点は注意してください。

HTML

<dialog class="modal" id="modal" open>
  <div class="modal__inner">
    <a href="index.html" class="modal__close">×</a>
    <img src="./dummy.jpg" alt="" class="modal__image">
    <div class="modal__text">
      <h2 class="modal__title">モーダル風画面</h2>
      <p>モーダル風画面の中身</p>
    </div>
  </div>
</dialog>

CSS

/* モーダル用スタイル */
.modal {
  border: none;
  background-color: transparent;
  margin: auto;
}
.modal__inner {
  background: #fff;
  padding: 0;
  border-radius: 16px;
  width: min(90vw, 500px);
  box-shadow: 0 2px 16px rgba(0,0,0,0.2);
  position: relative;
  font-size: 14px;
  font-weight: 400;
  overflow: hidden;
}
.modal::backdrop {
  background: rgba(0, 0, 0, 0.5);
}
.modal__close {
  position: absolute;
  top: 10px;
  right: 10px;
  text-decoration: none;
  background: none;
  border: none;
  font-size: 30px;
  cursor: pointer;
  color: #000;
  border-radius: 100vmax;
  width: 40px;
  height: 40px;
  background-color: #333;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: ease 300ms;
}
.modal__close:hover {
  background-color: #555;
}
.modal__text {
  padding: 1em 2em 2em;
}
.modal__title {
  font-size: 24px;
  font-weight: 600;
  margin-bottom: 0.5em;
}
.modal__image {
  aspect-ratio: 16/9;
  object-fit: cover;
  object-position: 50% 65%;
  width: 100%;
  height: auto;
  margin: 0;
  padding: 0;
}

次にモーダルを開く前のページにtemplate要素を追加します。

swup.jsの基本的な実装ケースでは

遷移開始アニメーション開始 → 遷移開始アニメーション終了 → コンテンツ切り替え → 遷移終了アニメーション開始 → 遷移終了アニメーション終了

というようなライフサイクルをとるのですが、モーダルの場合差し代わるコンテンツが存在しないため、コンテンツ切り替え前後の片方のアニメーションが丸ごと不要で、そのようなケースではtemplateを使ったプレースホルダーを用意してあげることで対応ができます。
※この時、要素の入れ子関係に注意してください。基本のswupコンテナの中身にtemplate要素が入るようにします。

<main id="swup" class="transition-main">
  <template id="modal"></template>
  <!-- 差し代わるコンテンツが入ります -->
</main>

最後にswup.jsの初期化時にオプションを指定します。

オプションの指定は遷移前/遷移後のURLルールと切り替わるコンテナーのID(classやネストされたセレクターはエラーになります)で指定します。

※うまくいかない場合はURLルールの指定ミスが怪しいです(経験者談)

// swup初期化
const swup = new Swup({
  containers: ['#swup'],
  plugins: [
    new SwupFragmentPlugin({
      rules: [
        {
          // モーダルウィンドウが開く前がindex.html、開いた後がmodal-open.htmlの場合
          from: ['/index.html', '/modal-open.html'],
          to: ['/index.html', '/modal-open.html'],
          containers: ['#modal'],
        }
      ]
    })
  ]
});

以下は実際に実装してみたページのサンプルです。

swup.jsでのモーダル風画面遷移のデモ

https://commono-swup-fragment-plugin-demo.netlify.app/

まとめ

swup.jsには他にも2つのページを一時的に共存させるParallel Plugin(→スライダーのような遷移ができる)や遷移先のコンテンツをあらかじめ読み込ませてラグの少ないページ遷移をさせることができるPreload Pluginなどが揃っており、使いこなせばかなり強力なライブラリです。

また、すでにお気づきの方もいらっしゃるかもしれませんが、今ご覧いただいている当社のWEBサイトもswup.jsを利用して非同期遷移を実装しています。今回、デザインテーマの一つとして「アプリライクな体験」というものがあったのですが、swup.jsを使うことでシームレスなページ遷移を比較的少ない工数で実現することができました。

機会があればswup.jsの別のプラグインや機能についても解説したいと思いますが、ぜひ実際に試してみてくださいね!

Written by

山本 慎二 Shinji Yamamoto

front-end engineer

1987年に東京で生まれたが、1歳からは北海道で育つ。
2010年に札幌市立大学デザイン学部製品デザインコースを卒業後、イベント運営を行う会社に勤務し業務の一環でHTML/CSSに触れる。
2016年にcommono立ち上げ時から参加し、フロントエンドエンジニアとして学びながら現在に至る。

Q1. commonoに参加した理由
矢野さんが立ち上げメンバーを探しているときに、知人の紹介でお会いし、ここでなら前向きにワクワクできるようなことができると感じて参加させていただきました。
Q2. わたしの偏愛
マイホーム(今年こそピザ窯を)
Q3. 今後commonoというフィールドでやりたいこと
クライアント様やデザイナーと一緒にユーザーの心に残るような体験をWEBで表現する手伝いをしていきたいです。

Recommend

お問い合わせ

    個人情報利用目的への同意

    ※このサイトはCloudflare Turnstileよって保護されています。
    ※お客様が入力した個人情報はSSL暗号化信号によって保護されています。

    個人情報の利用目的
    このお問い合わせフォームでご提供いただく個人情報は、お問合せ等に対する回答や資料送付、その他ご連絡のために利用します。
    当個人情報を第三者に提供することはありません。
    当個人情報の取扱いを委託することはありません。
    必要事項を全てご入力下さい。入力内容に不備がある場合は、ご返信しかねる場合がございます。
    内容により、お時間をいただく場合がございます。
    当個人情報の利用目的の通知、開示、内容の訂正・追加または削除、利用の停止・消去および第三者への提供の停止(「開示等」といいます。)を受け付けております。
    開示等の求めは、info@commono.co.jpで受け付けます。
    統計ツールやクッキー、ウェブビーコン等を用いてご利用状況を調査していますが、これによる個人情報の取得、利用は行っておりません。
    当社の個人情報保護に対する取り組みは「Privacy Policy」を必ずご確認ください。

    COMMONO株式会社
    E-mail:info@commono.co.jp
    個人情報保護方針、個人情報の取扱いについて
    PRIVACY POLICY

    現在準備中です

    お気に入り

    お気に入りから削除

    選択したアイテムをお気に入りから削除しますか?