NCMBとFramework7を使ったギャラリーコンポーネントの紹介と使い方

NCMBはモバイルアプリ開発におけるバックエンド機能（認証、データストア、ファイルストア、プッシュ通知など）を提供しています。バックエンドなのでAPIベースで利用するのが基本で、UI（アプリ側）は各自で開発する仕組みになっています。

現在、数多くのアプリが存在し、その中には定番とも言える機能があります。そうした定番機能を各フレームワークごとに実装しておくことで、再利用性高くNCMBが利用できるかと思います。

今回はFramework7で作った写真ギャラリーコンポーネントを紹介します。Monacaアプリでも利用可能です。

実際に動いているところはこちらです。

https://youtu.be/6DYvkBSdW_E

小さくて解像度が悪いですがGIFだとこんな感じ。

![gallery Resized.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/94ea4c55-e6df-d36e-94a4-8acb97a5336e.gif)


## UIについて

コンポーネントは1つのHTMLだけで実装されているのが特徴です。そのため、基本的には以下の方法で導入・利用ができます。

1. NCMB SDKの読み込み
1. NCMBのキーの取得
1. NCMBの初期化
1. ルーティングの設定
1. ギャラリーUI（HTML）を配置

用意されている画面（機能）は次の通りです。

### 写真一覧の表示

![FireShot Capture 068 - Gallery - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/a84b8d48-e29e-03bc-87f1-5d873e283efe.jpeg)


NCMBのファイルストアに保存されている写真を一覧表示します。

### 写真ブラウザー

![FireShot Capture 069 - 20220217205910 Gallery - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/eec31b82-f923-63d0-2264-fe3fc5462f08.jpeg)


タップした写真を拡大表示します。別な写真への切り替えもできます。

### 必要なライブラリ・SDKの読み込み

今回利用しているライブラリ・SDKは次の通りです。

- NCMB JavaScript SDK

これらを `www/index.html` で読み込みます。

```html
<script src="js/ncmb.min.js"></script>
```

### 必要なキー・トークンの取得

NCMBのアプリケーションキーとクライアントキーを取得します。

- [ニフクラ mobile backend](https://mbaas.nifcloud.com/)

### NCMBの初期化

`www/js/app.js` にてNCMBを初期化します。今回は `www/js/config.json` というファイルにキーを記述しているので、以下のように読み込みを行っています。

```js
const $ = Dom7;
(async () => {
  const device = Framework7.getDevice();
	// 設定ファイルの読み込み
  const config = await (await fetch('./js/config.json')).json();
	// NCMBの初期化
  window.ncmb = new NCMB(config.applicationKey, config.clientKey);

  // Framework7の初期化
  window.app = new Framework7({
    name: 'NCMB Notice',
    theme: 'auto',
    el: '#app',
    id: 'com.nifcloud.mbaas.map',
    store: store,
    routes: routes,
    input: {
      scrollIntoViewOnFocus: device.cordova && !device.electron,
      scrollIntoViewCentered: device.cordova && !device.electron,
    },
    statusbar: {
      iosOverlaysWebView: true,
      androidOverlaysWebView: false,
    },
    on: {
      init: function () {
        if (this.device.cordova) {
          cordovaApp.init(this);
        }
      },
    },
  });
})();
```

### ファイルストアの公開設定

NCMBの管理画面にて、ファイルストアのHTTPS公開設定を有効にします。これでファイルストアにアップロードした写真に対してHTTPS経由でアクセスできるようになります（キーが不要になります）。

![FireShot Capture 066 - Gallery - localhost.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/237ef3df-6199-fafc-b6cf-0518c3835979.jpeg)


### ルーティングの設定

今回は最低限のルーティングを設定しています（ `www/js/routes.js` ）。 `/gallery/:name` （nameはファイル名のプリフィックス） でギャラリーコンポーネントを表示します。

```js

var routes = [
  {
    path: '/',
    url: './index.html',
  },
  {
    path: '/gallery/:name',
    componentUrl: './pages/gallery.html',
  },
  {
    path: '(.*)',
    url: './pages/404.html',
  },
];
```

`www/index.html` で `/gallery/cat` を最初に表示します。この場合、ファイル名が `cat` で始まっている写真を表示します。

```html
<div id="app">
	<!-- Your main view, should have "view-main" class -->
	<div class="view view-init safe-areas" data-url="/gallery/cat">
	</div>
</div>
```

### ギャラリーUI（HTML）を配置

後は [gallery.html](https://raw.githubusercontent.com/NCMBMania/framework7_gallery_component/main/www/pages/gallery.html) をダウンロードして、`www/pages/gallery.html` に配置するだけです。

### 写真のアップロード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/39abf699-4b33-adfe-e7e6-4ccd875363ca.png)


任意の写真をアップロードします。今回は[キャットボックスから警戒する猫の写真素材 - ぱくたそ](https://www.pakutaso.com/20170146011post-9923.html)より猫の画像をお借りしています。写真の名前は `cat-〜.jpg` といった形に統一しています。

### アプリIDの取得

NCMBではHTTPSアクセスする際に次のようなURLでアクセスします。

```
https://mbaas.api.nifcloud.com/2013-09-01/applications/MKpbJPxaTIU5m1nW/publicFiles/cat-13.jpg
```

この時の `MKpbJPxaTIU5m1nW` はNCMBのアプリによって異なります（これをアプリIDと呼びます）。このアプリIDは管理画面のURLでわかります。

```
https://console.mbaas.nifcloud.com/#/applications/MKpbJPxaTIU5m1nW/file
```

この時の `MKpbJPxaTIU5m1nW` がアプリIDです。

### ギャラリーUIについて

ギャラリーUIの内容は次のようになります。実装はコメントを参照してください。 `YOUR_APP_ID` は自分のアプリIDに書き換えてください。 `/assets/images/blank.png` は [こちらより](https://raw.githubusercontent.com/NCMBMania/framework7_gallery_component/main/www/assets/images/blank.png)ダウンロードできます。

```html
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="title">フォトギャラリー</div>
      </div>
    </div>
    <div class="page-content">
      <div class="block gallery">
        <div class="row no-gap">
          ${ photos.map(photo => $h`
            <div class="col-25">
              <img src="${photo}" class="photo" @load=${load} @click=${show} />
            </div>
          `)}
        </div>
      </div>
    </div>
  </div>
</template>
<style>
  .gallery {
    display: none;
  }
  .photo {
    width: 100%;
    object-fit: cover;
  }
</style>
<script>
  export default function (props, {$f7, $onMounted, $update }) {
    const prefix = `${props.name}`; // 表示対象のファイル名（前方一致）
    const appId = 'YOUR_APP_ID'; // アプリID
    const blankPhoto = '/assets/images/blank.png'; // グリッドで余った部分の埋め合わせよう画像のパス
    const photos = []; // 取得した画像のURLを入れる変数
    let browser; // フォトブラウザオブジェクト
    let loadCount = 0; // 読み込み完了した画像の数
    $onMounted(async () => {
      reset();
      // ローディングアイコンを表示
      app.preloader.show();
      // 写真を取得します
      const files = await getPhotos();
      // ファイルを写真に変換します
      fileToPhoto(files);
      // フォトブラウザオブジェクトを作成します
      browser = $f7.photoBrowser.create({
        photos: photos.filter(photo => photo !== blankPhoto)
      });
      // 表示を更新
      $update();
      // ローディングを消します
      app.preloader.hide();
    });

    const reset = () => {
      // ギャラリーを一度非表示にします
      $('.gallery').hide();
      // 読み込み完了画像カウントのリセット
      loadCount = 0;
    }

    // ファイルを写真に変換します
    const fileToPhoto = (files) => {
      // 写真のファイル名から公開アクセス用URLに変換します
      files.forEach(file => {
        photos.push(`https://mbaas.api.nifcloud.com/2013-09-01/applications/${appId}/publicFiles/${file.fileName}`)
      });
      // グリッドの足りない分を空白の写真で埋め合わせます
      for (let i = 0; i < files.length % 4; i++) {
        photos.push(blankPhoto);
      }
    }

    // 画像が読み込まれると呼ばれる関数です
    const load = (e) => {
      // 読み込み完了画像のカウンターを1上げます
      loadCount += 1;
      // 全画像が読み込み終わるまで待ちます
      if (loadCount < photos.length) return;
      // 画像の幅を取得
      const width = $('.photo').width();
      // 全画像の高さを幅と同じ（正方形）にします
      $('.photo').css('height', `${width}px`);
      // ギャラリーを表示します
      $('.gallery').show();
    };

		// フォトブラウザで写真を表示します
    const show = (e) => {
      const photo = $(e.target).attr('src');
      const index = photos.indexOf(photo);
      if (photo === blankPhoto) return;
      browser.open(index);
    }

		// NCMBから写真を取得します
    const getPhotos = () => {
      return ncmb.File
        .regularExpressionTo('fileName', `${prefix}.*`)
        .limit(100)
        .fetchAll();
    }
    return $render;
  }
</script>
```

## 工夫したポイント

画像を正方形のグリッドで表示する機能がFramework7標準では用意されていないので、実装しています。画像のローディングをカウントして、全画像が読み込み終わったタイミングで幅を決定しています。

また、高さを決めるまではギャラリーを非表示にしています。もし表示すると、次のようになってHTMLっぽさが出てしまいます。

![before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/9ab23b44-00c0-9dea-4823-11617b36f705.gif)

高さを決定してから表示するとこんな感じです。

![after.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/7b7fd949-f7ff-553a-aea1-c36293932181.gif)


## カスタマイズ

たとえばギャラリーが複数あるならば `dog-〜.jpg` と名前をつけてアップロードして、HTML側でも `/gallery/god` にアクセスすれば良いでしょう。

## まとめ

ギャラリーコンポーネントを使えばアプリの中で写真一覧を実装するのがとても簡単に実現できます。ぜひお試しください！

[mBaaSでサーバー開発不要！ | ニフクラ mobile backend](https://mbaas.nifcloud.com/)