35
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

IonicAdvent Calendar 2018

Day 9

Ionicで半モーダルを作成する

Last updated at Posted at 2018-12-08

(この記事はIonicアドベントカレンダー2018の9日目の記事です。
この記事はKineca Developer Blogにアップしたものをアドカレ用に修正したものです。
興味ある方KinecaDeveloperBlogもぜひどうぞ!)

本記事は半モーダルをIonicで実装する方法について解説しています(最近急増中のあれです)
半モーダル知らない人は以下のリンクをクリック。
iOSにおける半モーダルビューの解釈

Ionicで半モーダルを実装する場合、次の手順を踏めば実装できます。

  1. ModalControllerを改造して実装する
  2. メインのコンテンツはion-footer内に記述する
  3. ion-contentは透過させるようにする
  4. 背景を黒くさせるbackdropを用意する

これらを実装すると、次のようになります。

※上の画像は弊社のエンタメアプリ「pato」に実装された様子になります。興味ある方は以下のリンクをクリック。
https://pato.today/

本記事では、上のような半モーダルを実装する方法を紹介しています。興味のある方は是非とも続きを読んでみてください。
**「ソースコードだけみたいんだ!」**って人は一番下にあるのでスクロールしてください。

新しくIonicアプリケーションを作る

新しくIonicアプリケーションを生成します。次のコマンドを実行してください。

ionic start ionic-semi-modal blank

生成が完了したら ionic-semi-modal ディレクトリに移動します。

cd ionic-semi-modal

次は、半モーダル用のページを生成しましょう

ionic g page semi-modal

また、その半モーダルをhome画面から呼び出せるようにする必要があります。

src/pages/home/home.tsを次のように変更します。

import { Component } from "@angular/core";
import { NavController, ModalController } from "ionic-angular";

@Component({
  selector: "page-home",
  templateUrl: "home.html"
})
export class HomePage {
  counter: number = 0;
  constructor(
    public navCtrl: NavController,
    public modalCtrl: ModalController
  ) {}

  showSemiModal() {
    const modal = this.modalCtrl.create("SemiModalPage", {
      counter: this.counter
    });
    modal.onDidDismiss(res => {
      if (res !== null) {
        this.counter = res;
      }
    });
    modal.present();
  }
}

合わせてsrc/pages/home/home.htmlsrc/pages/home/home.scssを次のように変更しましょう。

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic SemiModal
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <div class="counter-wrapper">{{counter}}</div>
  <button ion-button (click)="showSemiModal()">Show SemiModal</button>
</ion-content>
page-home {
  ion-content {
    text-align: center;
  }
  .counter-wrapper {
    font-size: 60px;
  }
}

半モーダルを作成する

半モーダル自体の作成は非常に簡単です。Ionicにはion-footerというコンポーネントがありこれを利用すれば簡単に半モーダルのUIを作成することができます。
src/pages/semi-modal/semi-modal.html、semi-modal.scssを以下のように書き換えましょう

<ion-content (click)="close()">
</ion-content>

<ion-footer padding>
  <div class="semi-modal-content">
    <button ion-button icon-only (click)="down()">
      <ion-icon name="remove"></ion-icon>
    </button>
    <div class="counter">
      {{counter}}
    </div>
    <button ion-button icon-only (click)="up()">
      <ion-icon name="add"></ion-icon>
    </button>
  </div>
  <button ion-button full color="secondary" (click)="save()">Save</button>
</ion-footer>
page-semi-modal {
  ion-content {
    opacity: 0;
  }
  ion-footer {
    background-color: #fff;
    .semi-modal-content {
      display: flex;
      justify-content: center;
      align-items: center;
      padding-bottom: 20px;
      .counter {
        font-size: 60px;
      }
      button {
        margin: 0px 12px;
      }
    }
  }
}

各ボタンに対するアクションも記述します。

import { Component } from "@angular/core";
import { IonicPage, ViewController, NavParams } from "ionic-angular";

@IonicPage()
@Component({
  selector: "page-semi-modal",
  templateUrl: "semi-modal.html"
})
export class SemiModalPage {
  counter: number = 0;

  constructor(public viewCtrl: ViewController, public navPrams: NavParams) {
    this.counter = this.navPrams.data.counter;
  }

  ionViewDidLoad() {
    console.log("ionViewDidLoad SemiModalPage");
  }

  up() {
    this.counter++;
  }

  down() {
    this.counter--;
  }

  close() {
    this.viewCtrl.dismiss(null);
  }
  save() {
    this.viewCtrl.dismiss(this.counter);
  }
}

以上、半モーダルのページが完成します。

バックドロップを自作する

半モーダルのページが完成しましたが、今のままだと背景が薄暗くなりません。単純に考えるとion-contentのbackdround-colorを変更すれば良さそうですが、その方法だとモーダルの遷移アニメーションと一緒に背景が移動してしまうという問題があります。

この問題に対応するためにbacdropを自作します。新しくBackdropProviderを作成しましょう。

ionic g provider backdrop

src/providers/backdrop/backdrop.tsを以下のように変更しましょう。

import { Injectable } from "@angular/core";

@Injectable()
export class BackdropProvider {
  constructor() {}

  show() {
    let backdrop = document.createElement("div");
    backdrop.id = "custom-backdrop";
    backdrop.className = "backdrop-fade-in";
    backdrop.style.position = "absolute";
    backdrop.style.top = "0";
    backdrop.style.right = "0";
    backdrop.style.left = "0";
    backdrop.style.bottom = "0";
    backdrop.style.width = "100%";
    backdrop.style.height = "100%";
    backdrop.style.background = "#000000";
    backdrop.style.opacity = "0.4";
    backdrop.style.display = "none";
    backdrop.style.pointerEvents = "none";

    let ionApp: any = document.getElementsByTagName("ion-app")[0];
    if (ionApp) {
      ionApp.appendChild(backdrop);
      backdrop.style.display = "block";
    }
  }

  hide() {
    let backdrop = document.getElementById("custom-backdrop");
    if (backdrop) {
      backdrop.classList.add("backdrop-fade-out");
      setTimeout(() => {
        backdrop.remove();
      }, 300);
    }
  }
}

backdropのfade-in、fade-outのアニメーションをsrc/app/app.scssに追加します。

.backdrop-fade-in {
  animation-name: fadeInOpacity;
  animation-timing-function: ease-in;
  animation-duration: 250ms;
  animation-fill-mode: both;
}

@keyframes fadeInOpacity {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 0.4;
  }
}

.backdrop-fade-out {
  animation-name: fadeOut;
  animation-duration: 200ms;
  animation-timing-function: ease-out;
  animation-fill-mode: both;
}
@keyframes fadeOut {
  0% {
    opacity: 0.4;
  }
  100% {
    opacity: 0;
  }
}

最後に、semi-modal表示時にbackdropを呼び出すようにします。src/pages/semi-modal/semi-modal.tsを以下のように変更しましょう。

import { Component } from "@angular/core";
import { IonicPage, ViewController, NavParams } from "ionic-angular";
+import { BackdropProvider } from "../../providers/backdrop/backdrop";

@IonicPage()
@Component({
  selector: "page-semi-modal",
  templateUrl: "semi-modal.html"
})
export class SemiModalPage {
  counter: number = 0;

+  constructor(public viewCtrl: ViewController, public navPrams: NavParams, public backdrop: BackdropProvider) {
    this.counter = this.navPrams.data.counter;
+    this.backdrop.show();
  }

  ionViewDidLoad() {
    console.log("ionViewDidLoad SemiModalPage");
  }

  up() {
    this.counter++;
  }

  down() {
    this.counter--;
  }

  close() {
+    this.backdrop.hide();
    this.viewCtrl.dismiss(null);
  }
  save() {
+    this.backdrop.hide();
    this.viewCtrl.dismiss(this.counter);
  }
}

完成

以上、半モーダルが完成しました。実際に完成したアプリケーションは下のように動作します。非常に簡単に実装できますね。

ソースコード

https://github.com/scrpgil/ionic-semi-modal
※気に入ったらスターをください

さいごに

この記事で紹介している実装は本番運用のアプリでも動いてます!
改めてアプリのリンクを載せますので興味のある方は触ってみてください!
https://pato.today/

また、patoを運営する株式会社キネカでは他にもIonicアプリを運用しております。
Ionic開発者の採用も積極的に行なっていますので興味のある方是非とも御声がけください。
それでは!

35
14
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
35
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?