‘プログラム’ カテゴリーのアーカイブ

1 2 3 4 5 6 21

家計簿ならぬ支出簿なアプリを作ってみた(2)

 プログラム
 公開日:2018年1月22日

 前回の投稿からちょっと間が空いてしまいましたが、
プログラムの中身について書いてみたいと思います。

 まずは、使用しているライブラリについて。

mithril

 仮想DOMを採用しているフレームワークです。
Angular とか React とかが有名どころだと思いますが
(自分は使ったこと無かったりします)
mithril は view に特化してシンプルな感じなのが気に入って使っています。

msgpack-lite

 JSONなデータを圧縮・展開してくれます。
というかバイナリ化したJSONのような感じ。
msgpack は各種言語に対応しているようです。

pikaday

 カレンダーをいい感じに表示してくれます。
ただし今回は独自に改造して使っています。

handsontable ( + numbro + pikaday )

 今回の肝。
このライブラリが無かったら作るのあきらめたかも(^_^;)
表計算のような見た目とUIを提供してくれるライブラリです。
無償版(CE)には full版 とそうでない版 があります。
full版は pikaday および numbro を内蔵しているのですが、
外部からは参照できないようになっています。
pikaday を直接的に使う必要があったため、
bareなhandsontable + numbro + pikaday という構成になっています。
なお、handsontable は日本語特有な対処が必要だったため、
独自に改造しています。

jaconv

 ひらがなカタカナ変換とかしてくれるライブラリです。

・calenderJp

 日本の祝日や元号とかに対応したカレンダーな自前ライブラリです。
拙作の「PDFなカレンダーを生成」で使ったPHPのを
JavaScriptで書き直したやつです。

 ときに、
支出簿なアプリの作成に着手したのは昨年の9月か10月頃だったりします。
handsontable は高機能な反面、結構バグが多いような印象です。
着手した頃は、特にIMEによる日本語入力周りで悩まされました。
この問題とか、autocompleteな入力において
「あいう」と入力したのに「ああいあいう」とか謎な事になったり。
ソースを読んで試行錯誤した結果、listen というAPIが怪しい・・・。
IMEが有効中に blur とかやるのが問題な感じ?
という所までは至ったのですが、結局改善できませんでした。

 そんなわけで、
どうにも埒が明かない感じなので、作るのを止めてしまいました。
数ヶ月ほど放っておいたのですが(^_^;)
ふと思い出してhandsontableの最新バージョンで試してみたら
日本語入力周りがうまく動くようになってました!

 おお! これは!!
ということで俄然ヤル気が起きて、また作業を開始しました。
しかしまだ色々問題が発覚し苦労したりしました。
例えば、あれこれそれ
CSSを書き換えたり、代替なオプションを試してみたりして
改善できたのもありますが、
いまだうまく行かないのもあったりします・・・。
内部的にCSSのoverflowを色々いじっているようなのですが、
これの副作用が潜在的な問題をはらんでいるような気がします。
handsontable は使いこなすのが大変ですわ(^_^;)

 その他にも、
仮想DOMなフレームワークと実DOMの整合性とか、
色々なんか苦労した感じですが、
最終的には結構使いやすい感じになったのではと、
個人的には思っています。

 次回は、
handontable や pikaday を独自に改造した部分について
書いてみたいと思います。

なお、
支出簿なアプリを試すにはこちらからどうぞ。

家計簿ならぬ支出簿なアプリを作ってみた

 プログラム
 公開日:2018年1月11日

 ふと、数ヶ月くらい前ですが、
何にどのくらいお金を使っているのかが気になり始めました。
ムダ使いとかしているつもりはないのですが、
なおざりな感じになっているなぁと・・・(^_^;)
1ヶ月にいくら、1ヶ年にいくらぐらい使っているのだろうか。
逆に言えば、どのくらいあればやっていけるのか分かれば、
使うべき所には使い、締めるべき所は締めるように
管理できたりして良さげではないかと。

 ということで、
家計簿なフリーソフトを探して使ってみたりしたのですが、
どうも自分にはしっくり来ない感じ。
表計算のような見た目で品目単位に入力するような
UIを大抵採用しているようなのですが、
個人的には領収証単位に入力したい。
あと、外税な場合は消費税を自動で計算してほしいし、
ポイント還元とかを使った場合の分類分けとかもしたい。

 そんなわけで、
支出の管理に重きを置いた、
家計簿ならぬ支出簿なアプリを作ってみました(^_^;)

特徴としては以下のような感じ。

・JavaScriptで作ってるのでブラウザがあればOK。
 サーバーに保存しないのでクライアント側だけで完結します。

・領収証単位の入力。
 店・施設等の名称を入力 ➔ 品目の入力 の流れになります。

・店名や品名の入力時に既存と部分一致する選択候補を随時表示。
 いわゆる、auto-complete な入力に対応してます。

・単価の入力時に過去の参考値を選択候補として随時表示。
 同じ品が過去にどの店でいくらだったのかを参照できます。

 ↓入力画面はこんな感じ。

↓領収証「店・施設等の名称」の入力画面。

 ↓「店・施設等の名称」入力と部分一致する選択候補を随時表示。

 ↓「品目」入力と部分一致する選択候補を随時表示。

 ↓「単価」入力時に過去の参考値を選択候補として随時表示。

 ↓「月計」の画面。

 ↓「年計」の画面。

 ↓「年計」画面でヘッダをクリックするとソートされます。

 ↓日付をクリックするとカレンダーが出て、任意の日へジャンプできます。
領収証を入力した日はアンダーバー表示されます。

 大体の見た目は以上です。

 「分類」の種類は現状固定です。
内部的な都合で変更や追加はできません。
ただ、いずれどうにかしたいとも思っています。

 なお、WindowsのChromeで動作確認できたら、
OKとしてしまっていますのでご了承ください。

 ということで、
支出簿なアプリを試すにはこちらからどうぞ。

 プログラムの中身については、おいおい書いてみたいと思います。

Mithril.js v1.x

 プログラム
 公開日:2017年3月20日

 Webアプリを作る際に、
フレームワークとか呼ばれるライブラリを使うと
それなりに便利です。
MVC(Model-View-Controller)を採用していることが多いかと思われます。

 JavaScriptなフレームワークの1つに
Mithril.jsというのがあります。
仮想DOMを導入しているフレームワークです。
そんなにメジャーではないようですが、
個人的には割と良さげな印象を持ってます。

 そんなMithrilですが、
2017年2月くらい(?)にバージョンが 1.x になったようです。
ただし、APIが改新されて 0.2.x との後方互換性が無くなっています。
やれやれ、APIを学習し直さないといけないのか・・・
とか思いましたが、
こちらのKey conceptsとか読んだら、考えが変わりました。

 ざっくり、どう変わったかと言えば、
アンチパターン回避の精神を推し進め、
MVCのうちのViewに重点を置き、
仮想DOMをより洗練させた。
といった感じでしょうか。

 フレームワークを使う場合、
設計者の意図するやり方に従う必要があります。
学習する手間はありますが、
慣れてくれば道に沿って進んで行けばよくなります。
一方、それは規約になり、好きなようにはやれなくなります。
機能が増えれば複雑性も増すかもしれません。

 Mithril v1.x はその辺を嫌っているようです。
より簡素に、また機能を集約し、
アンチパターン回避を促すべく、
ModelとControllerに縛りを設けないで、
柔軟にうまくやれるよ。
ということを目指しているようです。

 さて、
v0.2.xに比べてv1.xはどう変わったのかについて、
こちらのChange-Logを参照しつつ、
大きく変わったと思われる部分を中心に
詳しく見てみたいと思います。

m.prop()の削除。
 ModelとViewの間で双方向にバインディングするための機能ですが、
MVCの縛りは止めたので削除となったようです。
とは言え、それなりに便利な場合もあります。
その辺を考慮してか、
mithril/streamという代替ライブラリがオプションで用意されています。
ただ、それなりに高機能になってる感じなので、
単純にsetter/getterな機能だけがほしい場合は、
v0.2.xからコードを借りてきた方が早いかも。
↓こんな感じに。
m.component()の削除。
 従来は View + Controller でコンポーネントでしたが、
縛りを無くしたのでViewだけで構成できるようになりました。
仮想DOMを記述するための m() に統合されたので、
削除となったようです。
コンポーネント中にコンポーネントを記述できるようになったので、
柔軟性が増したかも。
config機能を改善してライフサイクルなイベントを導入。
  configは例えばcanvasの描画更新とかで使えたりしましたが、
より詳細に柔軟に対応できるように Lifecycle methods が導入されました。
従来Contorllerでやってた初期化は oninit()でやれるようになったり、
例えば oncreate()でcanvasのコンテキストを取得しておいて、
onupdate()で描画を更新とか細かくやれるようになりました。
仮想DOMだけでなくコンポーネントに対しても設定可能です。
再描画の挙動を変更。
 Mithrilでは再描画は自動で行われるので、
あまり気にする必要は無いのですが、
明示的に再描画を抑制したい場合があります。
従来は関数で制御していましたが、
結局イベントの延長で対処することになるので、
イベントのパラメータとして設定できるようになりました。
確かにこの方がスマートかも。

m.redraw()による再描画の挙動が、
即時に同期的に行う方式から、
予約して次の requestAnimationFrameのタイミングで
非同期に行うように変わりました。
むやみに m.redrawしたとしても無駄な描画は起きなくなりますね。
また従来は、
m.startComputation/ m.endComputationを使うことで
非同期処理が完了するまで描画を遅延させることができましたが、
アンチパターンの恐れを考慮して削除されました。
非同期処理後の再描画は m.redraw()を活用することになりそうです。
view()の引数やコンポーネントに対する引数の変更。
 viewの引数がcontrollerからvnodeに変わりました。
外部から渡すことになるコンポーネントに対する引数が、
オプション扱いから vnode.attrsとなりました。
さらに vnode.stateを使うことで、
コンポーネント内部の状態を管理できるようになりました。
外部からのが attrsで内部のが stateになるので分かりやすいですね。
m.deferredとm.syncを削除。
 いずれも非同期処理を扱うために用意されたものですが、
既に多くのブラウザでPromise対応が進んでいるので、
そっちに合わせます。ということで削除になった模様です。
非対応なブラウザであってもpolyfillで代替されるので問題ないです。
各ブラウザでのES6対応はどのくらい進んでいるんだろ、
と思ってここを参照してみたら、
IE11を無視すれば概ね対応が済んでるっぽい感じですね。

 その他にもいろいろありますが、
長文になったのでこの辺にしておきます(^_^;)
だいたい押さえたいところは書いたつもりです。

 ちなみに、
Mithril.jsを使って作ったのがこれ

アニメGIF⇒パラパラ漫画(JS版)

 プログラム
 公開日:2017年3月13日

アニメGIFからパラパラ漫画の冊子を作る
という記事に触発されて、
JavaScript版を作ったらどうだろうかと思い立ち、
まずはGIFをparseするライブラリを探してみるも、
個人的にしっくり来るのが見つからなかったので
改造して新たにライブラリを作ったりしました。

 次に必要なのはPDFを生成するライブラリ。
いくつかあるようなのですが、
クライアント側だけでうまくやれそうなのは
jsPDF一択という感じ。

 ただ、
こちらのドキュメントを見ると、
画像を扱うAPIが見当たらない・・・うむむ。
一方、こちらのLiveDemoでは画像を扱ってる・・・???。

 調べてみた所、
ドキュメントにはjsPDFのコアな部分についてのみ書いてあるようです。
画像を扱うための addImage なるAPIは
プラグインという形で提供されているようです。
こちらのGitHubにある、
"dist/jspdf.debug.js" および "dist/jspdf.min.js"
がプラグインを同梱しています。
前者は debug とかなってますが、minimizeされてないだけです。
debug時はこちらを使ってね、くらいな意味合いだと思います。

 さて、
jsPDF.addIamge()には dataURL な画像イメージを渡します。
読み込んだGIFファイルはcanvasに展開することになるので、
canvas.toDataURL()すればbase64なPNGを簡単に得ることが出来ます。
これでどうにかなりそう。
とか思ったんですが・・・実は苦労しました(^_^;)
コマ数が数十枚程度のアニメGIFを処理したら
ブラウザがメモリ不足エラーを起こしました・・・。
PNGをデコードしてPDFで扱えるようにしてるようなのですが、
イメージのサイズの総量が大きすぎるようです・・・。
addImageには圧縮のオプションがあるので、
指定してやってみたらエラーは起きなくなりましたが、
今度は時間が掛かることになってしまいました。

 jsPDFでPNGを扱うのはメモリ的&速度的に
ちょっと厳しいのかもしれません。
さてどうしたものか、と思いながら
addImageのソースを眺めていたら、
分かったことが2つありました。
ひとつはPNGだけでなくJPGも扱えること。
もうひとつは、
PNGはデコードした上で処理する必要があるが、
(圧縮指定があればさらにエンコードが必要)
JPGはほぼそのままで扱えるようになっていたこと。
PDFはJPGのイメージをそのまま扱えるっぽいようです。

 そんなわけで、
canvas.toDataURL('image/jpeg', 0.8)とかやって
addImageに渡すようにしました。
速度的にだいぶ改善された感じがします。
PNGのlosslessに対してJPGはlossyになってしまうので、
画質を設定できるようにしました。
またメモリ不足エラーが起きてしまった場合に備えて、
PDFを複数ページに分割して処理できるような工夫もしてみました。

 ちなみに、
jsなフレームワークの一つである Mithril.js
いつのまにかバージョン v1.0.xとかになってて、
APIとか一新されてました。
よい機会なので今回使ってみた次第です。
v0.2.xは使ったことあるのですが、
v1.0.xはいろいろ変わってたりしてました。
どう変わったかについては別途書いてみるつもりです。

 ということで、
アニメGIF⇒パラパラ漫画(JavaScript版)は
こちらから試せます。

ところで、
自分は印刷して切り出して冊子を作ったりしてなかったりします(^_^;)
あしからず。
実際にやってみた方の感想とかいただけるとうれしいかも。

GIFをparseするjsなライブラリ(3)

 プログラム
 公開日:2017年3月6日

 前々回でも書いていましたが、
アニメGIFを再現する際の
disposalMethodなる処理がよく分からず、
放っておいたままでした(^_^;)

 改めてさらに調査して試してみたら
うまく行った感じになったので、
それについてまとめてみたいと思います。

 こちらによると、
Disposal Method は0~3の4種類がある模様です。

 上記のように仕様が書かれているようですが、
実装するにはどうしたらよいのかがよく分からない感じです。

 こちらのページでは、
実際に0~3の値を指定して具体的なちがいを説明しています。
ところがGIFをダウンロードしてdisposalMethodの値を確認してみると、
0,1は良いのですが、
「2」となっているのは実際には3で、
「3」となっているのは実際には7が入っていました。
ということで残念ながら間違っている感じです。

 ところで、
disposalMethodはアニメGIFを再現するにあたっての、
現フレームから次フレームへ移行する際の
「後始末方法」を指示しているようです。
実装時には先頭以外の各フレームを描画する直前で対処することになりそうです。
値としては0~7を指定できるのですが、4~7は未定義っぽい?

 いろいろ調べた結果、
こちらのページを見つけました。
ページの右端に表示されてる5つのフレームを使って、
disposalMethodのちがいによる効果を説明しています。
2つのアニメGIFのdisposalMethodは、
それぞれ順に各フレームで
左:1,2,2,2,2
右:1,3,3,3,3
となっていました。





 これによって次のことが分かりました。
Disposal Method:
0) 特に何もしない。
1) 処理時には何もしないが、
 ”no disposal”という形でフレームの内容を覚えておく。
 一番最近に処理したのを1つだけ覚えておけばよい。
2) 全体を背景色でいったん塗りつぶす。
 ここで言う「背景色」はGIFヘッダでの設定を指しているのでは無く、
 描画側が設定する背景色を指します。
 通常は rgba(0,0,0,0) になるので、
 描画する範囲の既存の状態が背景色となります。
 特に意識しなくても問題ないと思います。
 たいていの場合はhtmlのbodyカラーが背景色になるはずです。
3) 2と同様に全体を背景色でいったん塗りつぶし、
 覚えておいた”no disposal”なフレームがあればそれを描画する。

 思ったより複雑ですね。
仕様書を読んだだけじゃ分からんンわ・・・(^_^;)

 ということで、
ライブラリを更新しました。
諸般の都合で一部の仕様が変わります(^_^;)
Gif.createFrameImages()は以下のように変わります。
第1引数にはCanvasの’2d’なコンテキストを渡します。
第2引数にtrueを渡すと、
あらかじめレンダリングしたイメージを生成します。
通常はこれが使いやすいかと思われます。
falseを渡した場合は、
GIF内のイメージのままで返します。
前フレームからの差分イメージになっていることがあります。
第3引数にfalseを渡すと、
canvasContext.putImageData()向けのイメージを生成します。
trueを渡すと、
canvasContext.drawImage()向けのイメージを生成します。

 サンプルなソースコードの例は以下のように変わります。

 拙作のライブラリを使って、
作ってみたツールはこちらから試せます。
ライブラリのダウンロードも行えます。

1 2 3 4 5 6 21