パズドラ練習アプリ機能追加

パズドラ練習アプリは2017/11月に作成した後ほったらかしにしていたのですが、パズドラで実施した大量の魔法石配布のおかげで奇跡的にゼラが当たり、2色陣の練習がしたいということで今回機能追加を行いました。

Onsen UIや勉強中のVue.jsを使って作り直してみたかったのですが、すでにjQueryを使いまくっていたこととWordPress上なので構成をシンプルにしたいということ、そしてできるだけ時間をかけたくなかったこととソースコードをみて眩暈がしたということで、今回はそのままjQueryとjQuery UIを使って作成しました。

jQueryはひさしぶりだったのですが、クラスセレクタや子孫セレクタ、FadeTo()などのアニメーション関連メソッドの便利さを実感しました。例えば、$( ‘#panel th’ ).removeClass( ‘selected’ );$(‘#panel th’).on(‘click’, function() {…}); といった短い記述で複数の要素を扱うことができます。JavaScriptだけで書くとdocument.querySelectorAll()の戻り値をforEach()のコールバックで処理する感じでしょうか。jQueryのシンプルな記述方法はいまだ魅力的に思います。

今回作成したパズドラ練習アプリは本記事の上部にあるリンク先、または倉庫 – パスドラ練習アプリにあります。

JavaScript Promiseとアニメーション

JavaScriptの非同期処理管理オブジェクトPromiseを使用してアニメーションを連続で行うサンプルを作成しました。

Promiseはほとんど使ったことがなかったのですが、今回requestAnimationFrameメソッドによるアニメーション関数をつなぐため使用しました。アニメーション関数から直接次の関数を呼んだりsetTimeoutメソッドを使ってつなぐこともできますが、Promiseを使うとアニメーション関数群の依存関係をなくすことができたり関数の呼び出しを一か所にまとめて記述できるので、保守性や可読性の向上が期待できます。

Promiseについて参考にしたサイト

アニメーションについて参考にしたサイト

サンプル1

See the Pen Promise anim by senmyou (@senmyou) on CodePen.

サンプル2

See the Pen Promise anim by senmyou (@senmyou) on CodePen.

サンプルについて

  • Arrayオブジェクトのreduceメソッドを使ってPromiseオブジェクトをつなぐ方法は、JavaScript Promiseの本の4.8. Promiseによる逐次処理やMDNの合成 (Composition)で解説されています。
  • requestAnimationFrameメソッドに渡すコールバック関数は、引数でタイムスタンプ(ミリ秒)を受け取ります。このタイムスタンプについてAPIのドキュメントを見てもわからないことがあり実験してみました。結果的にタイムスタンプの説明に次の2点を追記できると思います。
    • タイムスタンプはhtmlのページが読み込まれてから経過した時間(ミリ秒)を示す
    • タイムスタンプをプログラムから0に戻すことはできない

    また、これらのことからタイムスタンプはある基準点との差分をとって使うことになると思います。サンプルではアニメーションの基準(開始)点として初回のrequestAnimationFrameメソッドのタイムスタンプをstartTimeに保存して、毎回現在のタイムスタンプと差分をとって進捗度(progress)の算出に使用しています。

Promise備忘録

Promiseについての個人的な備忘録です。

  • Promiseのコンストラクタはexecutorと呼ばれる関数を引数にとる。
  • executorの引数にresolve関数とreject関数が渡される。基本的にexecutorで非同期処理を行い、そのなかでresolve関数またはreject関数を実行してPromiseオブジェクトの状態を変更させる。これが次段のコールバック関数の実行トリガになる。
  • executorはすぐに(コンストラクタがPromiseオブジェクトを返すよりも前に)実行される。これに対しthenメソッドへ渡すコールバック関数はすぐには実行されない。
  • thenメソッドはコールバック関数を登録してすぐに返る。この戻り値は新しいPromiseオブジェクトになるため、戻り値からthenメソッドを使うことでメソッドチェーンを作成することができる。
    例:promiseA.then(callbackA).then(callbackB).then(callbackC)
  • Promiseインターフェイスは作成時点では分からなくてもよい値へのプロキシ(代理)で、Promiseオブジェクトをthenメソッドによりハンドラ(コールバック関数)に関連付けることができる。

    Promiseオブジェクトをconsole.logで表示

    • PromiseオブジェクトはPromiseStatusプロパティとPromiseValueプロパティを持つ
    • コールバック関数がresolve()を実行すると対応するPromiseオブジェクトのPromiseStatusプロパティが”resolved”になる
    • これらのプロパティはプログラムから参照できないので実際は気にしなくていい
  • thenメソッドで登録したコールバック関数が値を返した場合は、暗黙的にPromise.resolve(値)によりPromiseでラップされる(値を返さない場合はundefinedをラップ)。続いて次段のコールバック関数が呼び出される。このとき引数に値が渡される。
  • thenメソッドで登録したコールバック関数がPromiseオブジェクトを返した場合は、そのPromiseオブジェクトがFulfilledまたはRejectedになるまで次段のコールバック関数は呼び出されない。この仕組みを利用して非同期処理の完了を待って次のコールバック関数を呼び出すことができる。
    (個人的なイメージ:コールバックから返されたPromiseオブジェクトがexecutorのresolve/reject関数によりFulfilled/Rejectedに状態を変えると、その状態がthenメソッドで生成したPromiseオブジェクトに伝搬し、関連付けられたコールバック関数が呼び出される)

Chart.js パン操作サンプル

Chart.jsでグラフをパン(PANまたはドラッグ、スクロール)操作するサンプルを作成しました。

Chart.jsはプラグインを読み込むことで機能拡張することができます。この拡張性・柔軟性の高さがChart.jsの人気の一因かと思います(メンテする方は大変そうですが)。プラグインは公式サイトでも公開されており、信頼性についても心配なさそうです。

Popular Extensions – chartjs.org

今回グラフをパン操作するにあたり、公式サイトで公開されているchartjs-plugin-zoomプラグインを使用しました。サンプルに加えこのプラグインの使い方をさらっと説明します。どなたかの参考になればと思います。

See the Pen Chart.js(pan) by senmyou (@senmyou) on CodePen.

プラグインの読み込み

chartjs-plugin-zoomプラグインではhammer.jsを利用しています。このためchartjs-plugin-zoomプラグインを読み込む前にhammer.jsを読み込む必要があります(読み込む順番が逆の場合エラーは出ませんがジェスチャーイベントがリスナーに登録されません)。

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/0.6.3/chartjs-plugin-zoom.js'></script>
if (Hammer) {
  var mc = new Hammer.Manager(node);
  mc.add(new Hammer.Pinch());
  mc.add(new Hammer.Pan({
    threshold: panThreshold
  }));
  .....
  mc.on('panstart', function(e) {
    currentDeltaX = 0;
    currentDeltaY = 0;
    handlePan(e);
  });
  mc.on('panmove', handlePan);
  mc.on('panend', function(e) {
    currentDeltaX = null;
    currentDeltaY = null;
    zoomNS.panCumulativeDelta = 0;
    setTimeout(function() { panning = false; }, 500);
  });

オプションの設定

  • options.pan.enabledとoptions.zoom.enabledをtrueに設定します。なにやらPANのみ行う場合もzoom.enabledをtrueにする必要があるみたいです(modeは空文字)。
    Pan doesn’t work without `zoom.enabled` #132

  • options.pan.rangeMin/rangeMaxにパン操作の有効範囲を設定します。パンやズーム操作時にrangeMin/rangeMaxに達すると、そこで操作を止めてくれます。設定するかどうかは任意で、設定しなくても問題なく動きます。

  • options.xAxes.min/maxにCanvas要素内に表示する範囲を設定します。これを設定しないと全データをCanvas要素いっぱいに拡大縮小して表示します。

options: {
  pan: {
    enabled: true,
    mode: "x",
    rangeMin: {
      x: rangeMin
    },
    rangeMax: {
      x: rangeMax
    },
  },
  zoom: {
    enabled: true,
    mode: ""
  },
  xAxes: [{
    type: 'time',
    time: {
      min: min,
      max: max
    },

最後に現在作成中のアプリで使用する予定のグラフを掲載します。データの作成部分はデバッグ中なのでオプションの設定だけ参考にして頂けたらと思います。Chart.jsはグラフのスタイリングを細かく設定できますしプラグインもたくさんあるのでデザインを決めるのに迷ってしまいますね。

See the Pen Chart.js(pan) by senmyou (@senmyou) on CodePen.