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

SakakibaraK
kineca-developer
Published in
13 min readDec 7, 2018

本記事は、現在急増中の半モーダルを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![output.gif](https://qiita-image-store.s3.amazonaws.com/0/165335/84edb598-be68-be68-bc51-7b5e9e612895.gif)
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/165335/a4bfde9d-fc12-fc23-d145-d770e90bffb9.gif)
</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
※いいねをください

さいごに

弊社ではpatoの他にもIonicアプリを運用しております。採用も全力で行なっていますので興味のある方是非とも御声がけください!

--

--