2
2

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.

「Monacaを使ってアニメの感想を記録できるアプリを爆速で開発しました」って言いたかった

Last updated at Posted at 2019-12-10

この記事は、Monaca Advent Calendar 2019の10日目の記事です。

間に合いませんでした。折を見て完成させたいです……。

あらまし

  • 普段は、ニフクラ mobile backendの企画 兼 営業として働いていて、なかなかコードを書く機会がない
  • けど、専門外なりにコードを書けるようにしておきたい
  • アドベントカレンダーの募集を見たので、いい機会だと思い書いてみることに

作ったもの

  • 年/クールごとにアニメの情報がズラッと並んでいて、クリックしたら詳細が見えるというもの
  • 本当は、感想やメモをクラウドに保存する仕組みを作りたかったが、間に合わず断念
    2019-12-10_17h02_27.png
    2019-12-10_17h02_42.png
    2019-12-10_17h02_53.png

使うもの

  • Monaca
    • Cordovaベースのハイブリッドアプリ開発環境。HTML / JSで簡単にスマホアプリが作れるすごいやつ
  • Onsen UI
    • 普通にHTML / JSを使ってアプリを作るとWebサイトっぽくなってしまって、アプリらしさがなくなっちゃうけど、これを使うとネイティブアプリのように見える便利なやつ。リファレンスのサイトが見やすい
  • ShangriLa Anime API

使いたかったもの(使えなかったもの)

  • ニフクラ mobile backend
    • Monacaと非常に相性のいいmBaaSサービス。3行のコードでデータをクラウドに保存できる
    • 間に合わず、自社の製品を組み込めないという失態……

構成

以下のようなファイル構成になりました。

www
L index.html
L main.html(最初の一覧画面)
L quarter_list.html(クォーターごとのアニメリスト)
L detail.html(アニメの詳細画面)
L script.js(jsの処理が詰め込まれています)

index.html

以下のような感じです。

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">

  <script>
    ons.ready(function() {
      console.log("Onsen UI is ready!");
    });

    if (ons.platform.isIPhoneX()) {
      document.documentElement.setAttribute('onsflag-iphonex-portrait', '');
      document.documentElement.setAttribute('onsflag-iphonex-landscape', '');
    }
  </script>
</head>
<body>
  <ons-navigator id="navi" page="main.html"></ons-navigator>
  <script src="script.js"></script>
</body>
</html>

ons-navigatorを使って画面遷移を実現しているので、タグを書いています。
IDはnavi、初期ページはmain.htmlです。
また、script.jsをさいごに読み込んでいます。

main.html

以下の様な感じです。

<ons-page>
  <ons-toolbar>
    <div class="center">
      アニメメモ
    </div>
  </ons-toolbar>
  <ons-list>
    <ons-list-header>2019年</ons-list-header>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2019/1'}})">1Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2019/2'}})">2Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2019/3'}})">3Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2019/4'}})">4Q</ons-list-item>
    <ons-list-header>2018年</ons-list-header>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2018/1'}})">1Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2018/2'}})">2Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2018/3'}})">3Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2018/4'}})">4Q</ons-list-item>
    <ons-list-header>2017年</ons-list-header>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2017/1'}})">1Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2017/2'}})">2Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2017/3'}})">3Q</ons-list-item>
    <ons-list-item tappable onclick="document.querySelector('#navi').pushPage('quarter_list.html', {data:{year_and_q: '2017/4'}})">4Q</ons-list-item>
  </ons-list>
</ons-page>

時間がなかったので完全にハードコーディングしています。
ShangriLa Anime APIは、以下形式でそのクォーターに放送されたアニメを一覧で取得できます。

http://api.moemoe.tokyo/anime/v1/master/yyyy(西暦)/q(クォーター)

そこで、最後のyyyy/qの部分をyear_and_qという変数で渡しつつ、quarter_list.htmlへ遷移しています。

quarter_list.html/detail.html

イベントリスナーでキャッチしてDOMに情報を流し込むので、HTMLファイルそのものは短いです。

quarter_list.html

<ons-page id="quarter_list">
  <ons-toolbar>
    <div class="center" id="title">
    </div>
    <div class="left">
      <ons-back-button></ons-back-button>
    </div>
  </ons-toolbar>
  <div id="quarter_list_html"></div>
</ons-page>

detail.html

<ons-page id="detail">
  <ons-toolbar>
    <div class="center" id="detail_title"></div>
    <div class="left"><ons-back-button></ons-back-button></div>
  </ons-toolbar>
  <h1 id="anime_title"></h1>
  <h2 id="hashtag"></h2>
  <h2 id="twitter"></h2>
  <p id="production"></p>
</ons-page>

script.js

ここにjsの処理を集約しています。

document.addEventListener('init', function(event) {
  var page = event.target;

  // quarter_list.htmlが読み込まれた時に発火
  if (page.id == 'quarter_list') {
    document.getElementById('title').innerHTML = page.data.year_and_q;
    print_list(page.data.year_and_q);
  }

  // detail.htmlが読み込まれた時に発火
  if(page.id == 'detail') {
    print_detail(page.data.year_and_q, page.data.id);
  }

});

// 指定されたクォーターの作品一覧を表示
function print_list(year_q){
  var xmlHttpRequest = new XMLHttpRequest();
  xmlHttpRequest.onreadystatechange = function(){
    if( this.readyState == 4 && this.status == 200 ){
      if( this.response )
      {
        var result = '';
        for(var i in this.response){
          result += '<ons-list-item tappable onclick="document.querySelector(\'#navi\').pushPage(\'detail.html\', {data: {id: \'' + i + '\', year_and_q: \'' + year_q + '\'}})">' + this.response[i]['title'] + '</ons-list-item>';
        }
        document.getElementById('quarter_list_html').innerHTML = '<ons-list>' + result + '</ons-list>';
      }
    }
  }
  xmlHttpRequest.open( 'GET', 'https://api.moemoe.tokyo/anime/v1/master/' + year_q, true );
  xmlHttpRequest.responseType = 'json';
  xmlHttpRequest.send( null );
}

// 指定された作品の情報を表示
function print_detail(year_q, id){
  var xmlHttpRequest = new XMLHttpRequest();
  xmlHttpRequest.onreadystatechange = function(){
    if( this.readyState == 4 && this.status == 200 ){
      if( this.response )
      {
        console.log(this.response[id]);
        document.getElementById('detail_title').innerHTML = this.response[id]['title'];
        document.getElementById('anime_title').innerHTML = this.response[id]['title'];
        document.getElementById('hashtag').innerHTML = 'ハッシュタグ: #'+ this.response[id]['twitter_hash_tag'];
        document.getElementById('twitter').innerHTML = 'Twitterアカウント: ' + this.response[id]['twitter_account'];
        document.getElementById('production').innerHTML = '制作会社: ' + this.response[id]['product_companies'];
      }
    }
  }
  xmlHttpRequest.open( 'GET', 'https://api.moemoe.tokyo/anime/v1/master/' + year_q, true );
  xmlHttpRequest.responseType = 'json';
  xmlHttpRequest.send( null );
}

まず、document.addEventListenerで、ページ遷移をキャッチします。
Onsen UIでは、ページが表示された際のイベントが'init'らしく、それをキャッチしています。
そして発火したページごとにprint_list()/print_detail()を実行するイメージです。

print_listでは、取得した作品一覧情報を、ons-listを使って整え、それをDOMに流し込んでいます。
また、詳細ページに遷移できるよう、tappableで、かつonclickを仕込んで、タップされたタイミングでpushPageを発火させるようになっています。

print_detailでは、クォーター情報とJSONの添字を受け取り、該当する作品の情報を表示します。

以上です。

よかったこと

  • 普段企画を担当している僕でも、拙い知識とリファレンスさえあれば、さほど詰まることなく開発を進められた点 完成させられなかったけど
    • Onsen UIのリファレンスは本当にわかりやすい
  • いいJSの復習になったこと
    • 開発者向けのサービスを提供していることもあり、やはり自分もたまには開発者の目線で色々なサービスを触ってみる必要があると痛感

いまいちだったこと

  • そもそも完成させられなかった点
    • 年末年始のお休みにでも完成させたいです
  • 画面を遷移するたびにAPIから情報を取得し直している点
    • 引数として取得結果のJSONを引き渡すなどして、API呼び出し回数を削減したい

まとめ

完成させられなかったのは残念でしたが、なんとかアプリとして動作するものは作ることができた。
ShangriLa Anime APIのようなAPIが無償で提供されているのは素晴らしいと思った。

ご覧頂きありがとうございました!

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?