- 投稿日:2019-05-29T12:32:55+09:00
IonicとFirebaseを使って3日で作る写真共有アプリ - Viewの作成 -
本記事は「IonicとFirebaseを使って3日で作る写真共有アプリ」シリーズの記事です。
まとめ記事↓
https://qiita.com/kazuki502/items/585aef0d79ed1235bec0アプリの画面を作るところを説明していきます。
これは私の感覚なんですが、アプリを作るときはまず画面と画面遷移から作ったほうがスムーズに進むと思っています。見た目を作ってから内部処理を作っていく感じですね。今回もそんな風に作っていきます。
Ionicのフォルダ構成について説明(少し理論的なので後から読んでもOKです)
画面を作っていく前に、Ionicのフォルダ構成について簡単に説明しておきます。Ionic(ベースとなっているAngularも同様)の基本単位はModuleと言われる塊です。まず一つの大きなModuleがあり、その中に複数のModuleが含まれる構成になっています。Moduleは何個でもModuleを含むことが出来て、ページ単位や機能単位でまとめるのが一般的な構成です。
ionic start
を実行した直後のフォルダ構成はこのようになっているはずです。src─┬─app─┬─home─┬─home.module.ts │ │ ├─home.page.ts │ ├─home.page.html │ ├─home.page.scss │ ├─app.module.ts ├─app.component.ts ├─app.component.html ├─app.component.scss ├─...この中には、2つのmoduleがあります。"app.module.ts"というmoduleの中に"home.module.ts"というmoduleが含まれているという関係になっています。
"app.module.ts"は、アプリ全体を包含しているroot moduleであり、"home.module.ts"はアプリの関連する機能を集約したfeature moduleとなっています。"app.module.ts"の直下にすべての機能を書くこともできるんですが、メンテナンス性が悪くなったり、パフォーマンスを改善しにくくなったりするので良くない構成です。"home.module.ts"のように適切に分割されていると、分かり易くなるし、"lazy load"という初期起動時間を短縮できるテクニックも使えるので、適切に分割するようにしましょう(初めは難しいですが…)。画面の構成
今回作る画面は画面です。
1. ホーム画面
2. 写真アップロード画面(モーダル画面)
3. 写真一覧画面
4. 共有された写真再生画面
※2.は操作性を踏まえてモーダル画面にしています。ホーム画面の作成
まずは各画面のハブとなるホーム画面から作っていきます。画面の構成から、3つの画面に遷移するためのボタンが必要だと分かります。IonicではデザインされたUI要素がいくつも用意されているので、自分で実装する前にそこに使えるパーツが無いか見てみましょう。
Ionic UI components
https://ionicframework.com/docs/components
ion-button
というのがあるので、これを"home.page.html"に追加してみましょう。ページ下部にある"Usage"に使い方の例があるので、"Angular"タブのコードを全部コピーしちゃいましょう。home.component.html<ion-header> <ion-toolbar> <ion-title> Ionic Blank </ion-title> </ion-toolbar> </ion-header> <ion-content> <div class="ion-padding"> The world is your oyster. <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p> </div> <!-- ここから追加した部分 --> <!-- Default --> <ion-button>Default</ion-button> <!-- Anchor --> <ion-button href="#">Anchor</ion-button> <!-- Colors --> <ion-button color="primary">Primary</ion-button> <ion-button color="secondary">Secondary</ion-button> <ion-button color="tertiary">Tertiary</ion-button> <ion-button color="success">Success</ion-button> <ion-button color="warning">Warning</ion-button> <ion-button color="danger">Danger</ion-button> <ion-button color="light">Light</ion-button> <ion-button color="medium">Medium</ion-button> <ion-button color="dark">Dark</ion-button> <!-- Expand --> <ion-button expand="full">Full Button</ion-button> <ion-button expand="block">Block Button</ion-button> <!-- Round --> <ion-button shape="round">Round Button</ion-button> <!-- Fill --> <ion-button expand="full" fill="outline">Outline + Full</ion-button> <ion-button expand="block" fill="outline">Outline + Block</ion-button> <ion-button shape="round" fill="outline">Outline + Round</ion-button> <!-- Icons --> <ion-button> <ion-icon slot="start" name="star"></ion-icon> Left Icon </ion-button> <ion-button> Right Icon <ion-icon slot="end" name="star"></ion-icon> </ion-button> <ion-button> <ion-icon slot="icon-only" name="star"></ion-icon> </ion-button> <!-- Sizes --> <ion-button size="large">Large</ion-button> <ion-button>Default</ion-button> <ion-button size="small">Small</ion-button> </ion-content>保存して
ionic serve
コマンドを実行すると下のような画面が出てきます。コードと画面を比べながら、好みのボタンを選びましょう。私は"LARGE"と書かれている大きなボタンが気に入ったので、これでボタンを作ることにしました。先ほどのソースから余計なものを消して、3つのボタンを作ります。
home.page.html<ion-header> <ion-toolbar> <ion-title> Ionic Blank </ion-title> </ion-toolbar> </ion-header> <ion-content> <div class="ion-padding"> The world is your oyster. <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p> </div> <ion-button size="large">ボタン1</ion-button> <ion-button size="large">ボタン2</ion-button> <ion-button size="large">ボタン3</ion-button> </ion-content>ボタンの名前は適当に付けています(後から変えていきます)。するとこのような画面になります。
いい感じに並んでくれていますね。このようにIonicを使うと、htmlだけでそれなりのデザインの画面が作れるのがメリットの一つです。(素のAngularを使うと、ボタンのスタイルや位置を整えるのにcssも触らないといけないので結構大変です。)
このままでもいいのですが、少し寂しいので背景画像を適当に差し替えてしまいましょう。
ここで少scssファイルを修正します。
home.page.scssion-content { --background: #fff url("画像のURL") no-repeat center center / cover; }そうするとこのように背景画像が設定されます。
これでホーム画面の実装は一旦ここまでにします。次は、他の画面も作っていきましょう。
写真アップロードモーダル画面の作成
次は、写真アップロードモーダル画面を作っていきます。モーダル画面ていうのは、もともと見ていた画面の上にかぶさるように表示される画面で、ユーザーが操作するまでは親画面を触らせないというものです。
このモーダル画面を一から実装しようとすると結構大変なんですが、Ionicにはモーダル画面を簡単に作れるコンポーネントが用意されています。
Ionic ion-Modal
https://ionicframework.com/docs/api/modalこのページにモーダル画面の設定方法が書いてあるのでそれに従って作っていきます。ざっとこんな手順です。
1. モーダル画面コンポーネントを作成する($ Ionic g component home/modal-upload
)
2. モーダル画面コントローラーを設定する
3. モーダル画面のスタイルを変更する順番に進めていきましょう。
モーダル画面コンポーネントを作成する
まずコンソール上で次のコマンドをたたいて下さい。
$ ionic g component home/modal-upload
homeフォルダの下に、modal-uploadフォルダが下記のような構成で作られます。
src─┬─app─┬─home─┬─home.module.ts ├─home.page.ts ├─home.page.html ├─home.page.scss └─modal-upload─┬─modal-upload.component.html ├─modal-upload.component.scss ├─modal-upload.component.ts
【コラム】 "page"と"component"の違い
Angularを勉強している方は気になったと思うので、ここで私なりの解釈をまとめておきたいと思います。Angularでは"page"というものは無くて、"component"が画面のコンポーネントとして使われているので混乱しやすいです(紛らわしいよね)。Ionicでは、ユーザーが見える画面を"page"という塊で作成し、その画面で使う要素を部品として外に出したものを"component"として作成しているという理解をしています。つまり、"component"単体で画面表示に使われることは無くて、"page"単体、もしくは、"page"と"component"のセットとして使われるということです。
ここら辺、もっと詳しい方がいたら教えてください!
ここまでで、モーダル画面自体は作成したのですが、まだ画面に表示できません。次は今作った画面をモーダル画面として表示する処理を設定していきましょう。
モーダル画面コントローラーを設定する
Ionicに標準装備されている、"modal-controller"を使うと面倒な実装をほとんどすることなくモーダル画面を設定することが出来ます。
ion-modalの説明ページのusageを参考に、"home.page.ts"ファイルを整えていきましょう。
home.page.tsimport { Component } from '@angular/core'; import { ModalController } from '@ionic/angular'; // 追加 import { ModalUploadComponent } from './modal-upload/modal-upload.component'; // 追加 @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage { constructor( public modalController: ModalController // 追加 ) {} // 追加メソッド↓ async presentModal() { const modal = await this.modalController.create({ component: ModalUploadComponent, // さっき作ったComponentを指定 componentProps: { value: 123 } }); return await modal.present(); } }home.page.html<ion-header> <ion-toolbar> <ion-title> Ionic Blank </ion-title> </ion-toolbar> </ion-header> <ion-content> <div class="ion-padding"> The world is your oyster. <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p> </div> <!-- ボタンの名前の変更とクリック時の処理を追加 --> <ion-button size="large" (click)="presentModal()">写真アップロード</ion-button> <ion-button size="large">ボタン2</ion-button> <ion-button size="large">ボタン3</ion-button> </ion-content>モーダル画面の方も設定していきましょう。
modal-upload.component.tsimport { Component, OnInit, Input } from '@angular/core'; // Inputを追加 import { NavParams } from '@ionic/angular'; // 追加 @Component({ selector: 'app-modal-upload', templateUrl: './modal-upload.component.html', styleUrls: ['./modal-upload.component.scss'], }) export class ModalUploadComponent implements OnInit { // "value" passed in componentProps @Input() value: number; constructor( navParams: NavParams // 追加 ) { } ngOnInit() {} }コピペだけですが、これでモーダル画面が立ち上がるようになりました。
ブラウザーで動作を確かめてみましょう。"写真アップロード"ボタンをクリックしてみてください。
はい、エラーになりました笑。何がいけなかったのか、エラーログを見てみましょう(エラーログをちゃんと読むと上達が早くなりますよ)。
core.js:15724 ERROR Error: Uncaught (in promise): Error: No component factory found for ModalUploadComponent. Did you add it to @NgModule.entryComponents? Error: No component factory found for ModalUploadComponent. Did you add it to @NgModule.entryComponents?「componenが無いですよー!@NgModule.entryComponentsにちゃんと入れましたか?」って言ってくれているみたいです。Ionic(Angularも)は、moduleが基本単位になっていて、アプリで使うソースはそこでimportして取り込んでおく必要があります。今回のエラーはその取り込み忘れが原因のようです。
"modal-upload.component"はホーム画面で使うための部品なので、"home.module.ts"に登録したいと思います。
home.module.tsimport { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { HomePage } from './home.page'; // 新しいimportを追加 import { ModalUploadComponent } from './modal-upload/modal-upload.component' @NgModule({ imports: [ CommonModule, FormsModule, IonicModule, RouterModule.forChild([ { path: '', component: HomePage } ]), ], declarations: [ HomePage, ModalUploadComponent // 追加 ], // entryComponentsを追加 entryComponents: [ ModalUploadComponent // 追加 ] }) export class HomePageModule {}保存して画面を立ち上げて、"写真アップロード"ボタンを押してみましょう。うまくいっていれば下のような画面ににゅっと出てくるはずです。
一般的なイメージモーダル画面に近づけるために、もう少しいじりましょう。
themeフォルダの"variables.scss"を編集します。variables.scss// Ionic Variables and Theming. For more info, please see: // http://ionicframework.com/docs/theming/ /** Ionic CSS Variables **/ :root { /** primary **/ --ion-color-primary: #3880ff; --ion-color-primary-rgb: 56, 128, 255; --ion-color-primary-contrast: #ffffff; --ion-color-primary-contrast-rgb: 255, 255, 255; --ion-color-primary-shade: #3171e0; --ion-color-primary-tint: #4c8dff; /** secondary **/ --ion-color-secondary: #0cd1e8; --ion-color-secondary-rgb: 12, 209, 232; --ion-color-secondary-contrast: #ffffff; --ion-color-secondary-contrast-rgb: 255, 255, 255; --ion-color-secondary-shade: #0bb8cc; --ion-color-secondary-tint: #24d6ea; /** tertiary **/ --ion-color-tertiary: #7044ff; --ion-color-tertiary-rgb: 112, 68, 255; --ion-color-tertiary-contrast: #ffffff; --ion-color-tertiary-contrast-rgb: 255, 255, 255; --ion-color-tertiary-shade: #633ce0; --ion-color-tertiary-tint: #7e57ff; /** success **/ --ion-color-success: #10dc60; --ion-color-success-rgb: 16, 220, 96; --ion-color-success-contrast: #ffffff; --ion-color-success-contrast-rgb: 255, 255, 255; --ion-color-success-shade: #0ec254; --ion-color-success-tint: #28e070; /** warning **/ --ion-color-warning: #ffce00; --ion-color-warning-rgb: 255, 206, 0; --ion-color-warning-contrast: #ffffff; --ion-color-warning-contrast-rgb: 255, 255, 255; --ion-color-warning-shade: #e0b500; --ion-color-warning-tint: #ffd31a; /** danger **/ --ion-color-danger: #f04141; --ion-color-danger-rgb: 245, 61, 61; --ion-color-danger-contrast: #ffffff; --ion-color-danger-contrast-rgb: 255, 255, 255; --ion-color-danger-shade: #d33939; --ion-color-danger-tint: #f25454; /** dark **/ --ion-color-dark: #222428; --ion-color-dark-rgb: 34, 34, 34; --ion-color-dark-contrast: #ffffff; --ion-color-dark-contrast-rgb: 255, 255, 255; --ion-color-dark-shade: #1e2023; --ion-color-dark-tint: #383a3e; /** medium **/ --ion-color-medium: #989aa2; --ion-color-medium-rgb: 152, 154, 162; --ion-color-medium-contrast: #ffffff; --ion-color-medium-contrast-rgb: 255, 255, 255; --ion-color-medium-shade: #86888f; --ion-color-medium-tint: #a2a4ab; /** light **/ --ion-color-light: #f4f5f8; --ion-color-light-rgb: 244, 244, 244; --ion-color-light-contrast: #000000; --ion-color-light-contrast-rgb: 0, 0, 0; --ion-color-light-shade: #d7d8da; --ion-color-light-tint: #f5f6f9; } /** 追加 **/ .modal-wrapper { --width: 80vw; --height: 60vh; --border-radius: 20px; }すると、さっきのモーダル画面がこんな風に変わります。
よく見る形になりました。後は、モーダル画面に共有する画像を選択するinputと送信ボタンを追加しておきましょう。デザインは後で整えるのでテキトーで大丈夫です。
modal-upload.component.html<p> modal-upload works! </p> <label for="photo">画像を選択</label> <input type="file" name="photo" id="photo-upload"> <ion-button>送信</ion-button>こんな感じになります(かなりがたがたですが、今はそれで大丈夫です)
写真一覧ページを作成
共有された写真を一覧で見るためのページを作っていきます。これは、ホーム画面とは別の新しいページなので次のコマンドで新しい"page"を作りましょう。
$ ionic g page photo-list
このコマンドで"page"を作ると、自動的にmoduleに追加してくれるのでかなり便利です。こんな感じで追加されているのが確認できます。
app-routing.module.tsimport { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', loadChildren: './home/home.module#HomePageModule' }, // 'photo-list'が追加されている { path: 'photo-list', loadChildren: './photo-list/photo-list.module#PhotoListPageModule' }, ]; @NgModule({ imports: [ RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) ], exports: [RouterModule] }) export class AppRoutingModule { }Ionicはページ遷移もhtmlだけで簡単に設定できます。
home.page.html<ion-header> <ion-toolbar> <ion-title> Ionic Blank </ion-title> </ion-toolbar> </ion-header> <ion-content> <div class="ion-padding"> The world is your oyster. <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p> </div> <ion-button size="large" (click)="presentModal()">写真アップロード</ion-button> <!-- ボタン名の変更とrouterLinkを追加 --> <ion-button size="large" routerLink="/photo-list">写真一覧</ion-button> <ion-button size="large">ボタン3</ion-button> </ion-content>これで、写真一覧ボタンを押すと画面が切り替わるようになりました。
ただ、このままだと写真一覧画面に映ったままホーム画面に戻れないので、"戻るボタン"もつけましょう。実はこれもすでに用意されています。
ionoic ion-back-button
https://ionicframework.com/docs/api/back-buttonusageを見ると、
<ion-header>
の中にいれればいいみたいなので素直にそうしてみます。photo-list.page.html<ion-header> <ion-toolbar> <!-- この部分を追加 --> <ion-buttons slot="start"> <ion-back-button></ion-back-button> </ion-buttons> <ion-title>photo-list</ion-title> </ion-toolbar> </ion-header> <ion-content> </ion-content>これでもう一回見てみると、画面の左上に"Back"というボタンが追加されていると思います。
※ホーム画面から遷移しないと出現しないので、ブラウザーのurlの"/photo-list"を消してから操作してください。これで、ホーム画面に戻ることが出来るようになりました。
これに写真を追加してこんな風にしていきましょう(下図)。
こんな画面にするにはどのようにすればいいでしょうか?
(考える時間…)
(考える時間…)
(考える時間…)
(考える時間…)
はい、その通りIonicのコンポーネントを使っていけばいいですね。このコンポーネントを使っていきましょう。
Ionic ion-img
https://ionicframework.com/docs/api/imgIonic ion-grid
https://ionicframework.com/docs/api/gridこんな風にhtmlをいじることでこの画面にすることが出来ます。
photo-list.page.html<ion-header> <ion-toolbar> <ion-buttons slot="start"> <ion-back-button></ion-back-button> </ion-buttons> <ion-title>photo-list</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-grid> <ion-row> <ion-col> <ion-img src="https://img.co-trip.jp/content/14renewal_images_l/290282/main_image.jpg"></ion-img> </ion-col> <ion-col> <ion-img src="https://img.co-trip.jp/content/14renewal_images_l/290282/main_image.jpg"></ion-img> </ion-col> </ion-row> <ion-row> <ion-col> <ion-img src="https://img.co-trip.jp/content/14renewal_images_l/290282/main_image.jpg"></ion-img> </ion-col> <ion-col> <ion-img src="https://img.co-trip.jp/content/14renewal_images_l/290282/main_image.jpg"></ion-img> </ion-col> </ion-row> </ion-grid> </ion-content>
- 投稿日:2019-05-29T02:58:17+09:00
CSSである要素の上下左右に表示されるように要素を配置したい
HTMLとCSSで狙った位置に要素を配置するのにいつも数時間は悩む初心者です。
あるコンテンツの上もしくは下もしくは左もしくは右に表示されるように要素を配置したい。(tooltipとかballoonとかdropdownなリストを表示したい)
この時に以下も考慮しておきます。
- コンテンツに被らないように配慮したい(他のコンテンツには被っても良い)
- 上下に配置するとき横幅はコンテンツの長さに合わせたい
- 左右に配置するときはコンテンツの上か下に沿わせたい
Demo: https://jsfiddle.net/nL0brfht/2/
こんなふうに見えます。一応Firefox,Chrome,IE(Edgeではない)で大体同じ見え方をすることを確認しました。
やったことは単純でこんな感じです。
- 1個wrapper要素で包み、
position: relative
を与える- 上下左右に配置する要素はwrapper内に配置。
position: absolute
を与えることで、wrapper要素の位置を基準に配置できるようにする
- 後はtop,left,right,bottomを使っていい感じに調整する
細かい考慮点を満たすメモです。
- コンテンツに被らないように配慮したい(他のコンテンツは被っても良い)
- コンテンツの上に沿わせたい場合は、
bottom: 100%
- (100%はコンテンツの高さとなり、bottom:100%とすることで上方向に移動します。で、y座標がwrapperのy座標と一致するので、いい感じになるという感じです。他も同じ原理です。)
- コンテンツの下に沿わせたい場合は、
top: 100%
- コンテンツの左に沿わせたい場合は、
right: 100%
- コンテンツの右に沿わせたい場合は、
left: 100%
- 上下に配置するとき横幅はコンテンツの長さに合わせたい
left: 0; right:0;
にします- 左右に配置するときはコンテンツの上か下に沿わせたい
- コンテンツの上に沿わせたい場合は、
bottom: 100%
- コンテンツの下に沿わせたい場合は、
top: 100%
欲を言えば、領域に余裕がある方向を動的に決めて出したいですね。
- 投稿日:2019-05-29T01:48:11+09:00
color_fieldとインラインでの擬似要素の表示について
吹き出しの背景色をその時の気分で色を決めたい!
変更前がこちら
やること
Articleモデルに色を登録するカラム
color_1
とcolor_2
を追加するcolor_field
jqueryのカラーピッカーとかを使って色を登録しようかなんて思っていたところRailsが提供している
color_field
というものを見つけた。これで色を選択できる!こんなかんじ↓
_form.html.erb<tr> <th scope="row">吹き出し1</th> <td><%= f.text_field :balloon_1, size: "90x10" %> <%= f.color_field :color_1 %> </td> </tr> <tr> <th scope="row">吹き出し2</th> <td><%= f.text_field :balloon_2, size: "90x10" %> <%= f.color_field :color_2 %> </td> </tr>色の表示
<div class = "balloon1" style="background-color:<%= @article_.color_1 %>"><div class = "balloon1" style="background-color:#b8e1f5">これだとまだ吹き出しの3角の部分の色がstyle.cssで指定したままの色なので未完成です。
この吹き出しのCSSはこのようなコードでできています。style.css.detail .article .balloon1{ position: relative; padding: 20px; border-radius: 10px; background-color: #ffb1cd99; width: 75%; height: 25px; } /* 三角アイコン */ .balloon1::before{ content: ''; position: absolute; display: block; width: 0; height: 0; left: -15px; top: 17px; border-right: 15px solid #ffb1cd99; border-top: 15px solid transparent; border-bottom: 15px solid transparent; }::beforeってのが擬似要素ってやつでこれのスタイル指定を別で行わなきゃいけないようです。
擬似要素ってなあに?って方はこちら↓
この方の記事がわかりやすかったです!
いろんな仕様の変更とかで::
と:
が混じってわかりにくい記事も多いです
参考:擬似要素と擬似クラス<style>.balloon1::before { border-right: 15px solid <%= @article_.color_1 %>; }</style> <div class = "balloon1" style="background-color:<%= @article_.color_1 %>"><%= @article_.balloon_1 %></div>できた!