1
0

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 1 year has passed since last update.

Monaca × NCMBでマンガビューワーアプリを作る

Last updated at Posted at 2022-05-24

NCMBにはいくつかの機能がありますが、データベースのように使えるデータストアや写真などを保存しておくファイルストアはよく使われる機能になります。

今回はそのデータストアとファイルストアを組み合わせて、マンガビューワーアプリアプリを作ります。まず初回となる今回は、アプリの概要とNCMBの初期化を行います。

完成版のコード

作成したデモアプリのコードはNCMBMania/Monaca_Comic: MonacaとNCMBを使ったマンガアプリのサンプルですにアップロードしてあります。実装時の参考にしてください。

ベースについて

今回はMonacaを利用し、UIフレームワークとしてFramework7を用いています。その際のベースとしてNCMBMania/MonacaFramework7Base: Monaca × Framework7 × NCMBのベースアプリです。を利用しています。執筆時点でのFramework7のバージョンは7.0.2です。

このベースアプリにFramework7のUI部分(NCMB部分除く)を実装したハンズオンテンプレートが下記になります。まず、これをダウンロードして、Monaca IDEにてアップロードします。

NCMBMania/Monaca_Comic_Handson: Monacaを使ったマンガアプリのハンズオンデータです

画面について

今回のアプリは以下の2つの画面を持っています。

  • 書籍一覧画面
  • 書籍閲覧画面

書籍一覧画面

FireShot Capture 282 - 20220506185731 Monaca × Framework7 × NCMB - localhost.jpg

pages/list.html として作成します。データストアから書籍データを取得し、一覧表示します。

書籍閲覧画面

FireShot Capture 282 - 20220506185729 Monaca × Framework7 × NCMB - localhost.jpg

pages/view.html として作成します。一覧画面で選択された書籍の画像データを取得し、マンガビューワーで表示します。

利用するライブラリ

今回は以下のライブラリ・SDKを利用しています。

NCMB SDKとFramework7は上記ベースアプリに元々含まれています。laymicはWebベースのマンガビューワーになります。

laymicのインストール(実装済み)

laymicをGitHubのリポジトリからダウンロードし、distの中身を www/assets/laymic 以下に保存します。そして www/index.html にて読み込みます。

<!-- 記述済み -->
<link rel="stylesheet" href="assets/laymic/laymic.min.css">
<script src="assets/laymic/laymic.iife.min.js"></script>

ルーティングの設定(実装済み)

今回の2画面に合わせてルーティングを変更します。 js/routes.js を以下のように修正します。

// 記述済み
const routes = [
  {
    path: '/',
    url: './index.html',
  },
  // コミック一覧画面
  {
    path: '/comics/',
    componentUrl: './pages/list.html',
  },
  // コミック詳細画面
  {
    path: '/comics/:key',
    componentUrl: './pages/view.html',
  },
  {
    path: '(.*)',
    url: './pages/404.html',
  },
];

index.htmlの修正

index.htmlでは初期表示でコミック一覧画面を読み込むように修正します。まだ pages/list.html はないので、画面が真っ白になってしまいます。

<!-- 記述済み -->
<div id="app">
	<div class="views tabs safe-areas">
      <div id="view-home" class="view view-main view-init" data-url="/comics/">
      </div>
    </div>
  </div>
</div>

NCMB SDKの初期化

NCMBのWebサイトでアプリを作成し、アプリケーションキーとクライアントキーを取得します。そして www/js/config.json を開いてそれぞれのキーを記述します。

{
	"applicationKey": "ここにアプリケーションキー",
	"clientKey": "ここにクライアントキー"
}

この config.jsonjs/app.js にて読み込まれ、NCMBを初期化処理に利用されます。

// 記述済み
// NCMBの初期化用
const event = window.cordova ? 'deviceready' : 'DOMContentLoaded';
document.addEventListener(event, async (e) => {
  const config = await (await fetch('./js/config.json')).json();
  window.ncmb = new NCMB(config.applicationKey, config.clientKey);
  window.app = new Framework7({
    name: 'My App', // App name
    theme: 'auto', // Automatic theme detection
    el: '#app', // App root element
    // App store
    store: store,
    // App routes
    routes: routes,
  });
});

これでNCMBが初期化され、利用準備が整いました。

データの用意

マンガデータ

今回はCCなどで公開されている以下のマンガデータを利用しました。ありがとうございます。

これらのデータをダウンロードし(一部はPDFから画像にした上で)、ページ毎に連番で保存します。たとえばブラックジャックによろしくであれば、bj_1.jpeg・bj_2.jpeg…といった具合です。

そうしたデータをまとめてNCMBのファイルストアにアップロードします。

FireShot Capture 276 - 20220506155402 ニフクラ mobile backend - console.mbaas.nifcloud.com.jpg

※ ハンズオン用に画像データを以下にアップロードしてあります。ダウンロード、展開してください。

images.zip

データストア

次にデータストアでComicクラスを作成します。フィールドは次のように作成します。

フィールド名 意味
title 文字列 マンガのタイトル
key 文字列 画像ファイル名のプリフィックス(bj_など)

そしてマンガデータを作成します。

FireShot Capture 277 - 20220506155412 ニフクラ mobile backend - console.mbaas.nifcloud.com.jpg

以下は例です。

title key
ブラックジャックによろしく bj_
ダーウィン事変 darwinsincident_
聖☆おにいさん sym001_p01

一覧画面の作成

www/pages/list.html を作成します。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="list links-list">
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $onMounted, $update }) => {
		// ここにJavaScriptを記述
    return $render;
  };
</script>

書籍データを取得する

JavaScriptで書籍データを取得する処理を記述します。これは $onMounted 関数内で記述します。この関数はDOMのマウントが終わった際に呼ばれます。

// 要記述
let comics = [];
// DOMがマウントされたタイミングで実行
$onMounted(async () => {
	// 1. コミックデータを取得する
	const Comic = ncmb.DataStore('Comic');
	comics = await Comic
		.order('title')
		.fetchAll();
	// 取得したら表示更新
	$update();
});

変数 comics の中にデータが入った段階で $update 関数を呼ぶことで、HTML側に表示を反映できます。 div.list の中に一覧表示を追加します。

<!-- 記述済み -->
<div class="list links-list">
	<!-- ここに追加(ここから) -->
	<ul>
		${ comics.map(comic => $h`
			<li><a href="/comics/${comic.key}?title=${comic.title}">${comic.title}</a></li>
		`)}
	</ul>
	<!-- ここに追加(ここまで) -->
</div>

これでデータの取得と、画面への反映が完了します。

FireShot Capture 282 - 20220506185731 Monaca × Framework7 × NCMB - localhost.jpg

ビューワー画面の実装

www/pages/view.html を作成します。HTMLを含めた基本形は次の通りです。

<!-- 記述済み -->
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="left">
          <a href="#" class="link back">
            <i class="icon icon-back"></i>
            <span class="if-not-md">戻る</span>
          </a>
        </div>
        <div class="title">${ title }</div>
      </div>
    </div>
    <div class="page-content">
			<div class="laymic" data-viewer-id="laymic">
			</div>
		</div>
  </div>
</template>
<script>
  export default (props, { $f7route, $on, $f7router, $update, $tick }) => {
		// ここにJavaScriptを記述
    return $render;
  };
</script>

変数を準備

まず画面で表示するための変数を準備します。基本的に一覧画面から送られてきた変数を受け取るだけです。

// 記述済み
const { key } = props; // 検索キー
const { title }  = $f7route.query; // 画面表示用のマンガタイトル
let pages = []; // マンガ画像が入る配列

画面表示処理

画面表示時のイベントでは、以下の処理を行います。

  1. ローディングアイコンの表示
  2. ファイルストアからの画像取得
  3. マンガビューワーの立ち上げ
  4. ローディングアイコンの非表示

コードで書くと、次のようになります。

// 記述済み
// 画面を表示する前に実行されるイベント
$on('page:beforein', async () => {
	// ローディング表示
	app.preloader.show();
	// ファイルストアのデータを取得
	const blobs = await getPage();
	// ファイルストアのデータをblob形式に変換
	pages = blobs.map(p => URL.createObjectURL(p));
	// 表示更新
	$update();
	// 描画完了を待つ
	await $tick();
	showLaymic();
	// ローディングを消す
	app.preloader.hide();
});

ファイルストアからの画像取得

ファイルストアからは、先ほどの key を使って取得対象の画像を絞り込みます。また、ファイル名ごとに並べることでページ番号順に取得しています。

ファイルストアを検索しただけではデータのダウンロードまでは行っていませんので、さらに ncmb.File.download を使ってダウンロード処理も行っています。

// 要記述
// ファイルストアからデータを取得する関数
const getPage = async () => {
	// 1. ファイル名のキーで検索
	const files = await ncmb.File
		.regularExpressionTo('fileName', `^${key}.*`)
		.order('fileName', false)
		.fetchAll();
	// 2. 写真データをblob形式でダウンロード
	const promises = files.map(f => ncmb.File.download(f.fileName, 'blob'));
	// Promiseで処理を返す
	return Promise.all(promises);
};

マンガビューワーの立ち上げ

マンガビューワーlaymicの立ち上げ処理は次のようになります。注意点としては、Framework7の画面が邪魔をしてビューワーが後ろに開いてしまうことです。そのため、今回はlaymicが開いた段階で一旦Framework7全体を非表示にし、laymicを閉じた段階で表示を戻すと言ったことを行っています。

// 記述済み
// 電子書籍を表示する処理
const showLaymic = () => {
	// Laymicの表示
	const applicator = new laymic.LaymicApplicator(".laymic", {
		pageWidth: 690,
		pageHeight: 976,
	});
	applicator.open('laymic');
	// Framework7自体を一旦隠す
	app.el.style.display = 'none';
	// 電子書籍を閉じる際にFramework7を表示する
	$('.laymic_close').on('click', e => {
		app.el.style.display = 'block';
		// 一覧ページに戻る
		$f7router.back();
	});
}

画面描画内容

pages 変数にはblob:ではじまるURLが配列になっています。これを画面上に表示します。

<!-- 記述済み -->
<div class="laymic" data-viewer-id="laymic">
	<!-- 追記(ここから)-->
	${ pages.map(page => $h`
		<img data-src="${page}" />
	`)}
	<!-- 追記(ここまで)-->
</div>

さらに標準のCSP(Content-Security-Policy)ではblobが使えないので www/index.html を修正します。

<!-- 記述済み -->
<meta http-equiv="Content-Security-Policy" content="default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content: blob:"> <!-- 最後に blob: を追加-->

これでマンガビューワーが立ち上がるでしょう。

FireShot Capture 282 - 20220506185729 Monaca × Framework7 × NCMB - localhost.jpg

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?