2015年4月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
無料ブログはココログ

カテゴリー「enchantMOON」の記事

2015年2月16日 (月)

telepathy で遊ぼう - 手書きテレパシーのプロトタイプ

MOONPhase ver.3.0.0 で実装された telepathy 機能、面白そうですね。

うまく使えばキラーアプリができるんじゃないかという、「何か化けそう」なオーラを感じます。昨年末に ver.3.0.0 が出てから、何か作りたいなと思っていました。

ただ、「何をテレパしったら面白いのか」「そもそも、telepathy 機能って、どうすれば使えるんだろう」というところで、随分時間がかかりました。特に後者。

MOONBlock からだと、やりたいことが思いつかない。MOONBlock 経由せずに telepathy が使えるのかわからない。どう実装されてるのかな、と思って、.lib/stickerlib.min.js を読んでみたんですが、これが複数の *.js を minify してあるので、もう読みにくい読みにくい。

ここ から、ver.2.8.0 用の新規プロジェクトファイル をダウンロードして、lib/puppet.enchant.js と、lib/moon.puppet.enchant.js を読みながら、lib/stickerlib.min.js を整形して対比していけばよいことに気付いて、一気に進みました。というか、気付くまで、全然進まなかった…。

ということで、ようやくひとつ作りました。

「手書きテレパシー プロトタイプ」をダウンロード

使い方

  • シールをタップします。
  • MOON の画面上に、好きな線や文字を書きます。
  • [Clear] ボタンをタップすると、書いた線が全て消去されます。
  • [Send] ボタンをタップすると、書いた線が telepathy に送信されて、その後消去されます。
  • 直後に telepathy で受信されて、ページのストロークとして保存されます。
  • 「ひとり」で遊んでいると、自分の書いた線が保存されるだけです。

が、

  • 同時にシールを実行している人が複数いると、全員の書いた線がかぶさってきます。
  • なんてステキで、おもしろい???

実装は乱暴で、プロトタイプです。なぜプロトタイプかというと

このシール、テレパシーとして面白いかどうか、「どこのお宅にもある enchantMOON を 2 台以上」使わないとわからないんです。面白いのかな ? つまらんのかな ? Singularity かも ? とか、全然わからない。

面白いなら、いろいろ追及してみてもいいんですが、わからないので、とりあえずプロトタイプ。なので実装もすごく乱暴です。UI も最低限で、線に色や強弱をつけることもできない。telepathy のチャンネルも固定で変えられないし、lib/stickerlib.min.js を直に書き換えたりもしている。

絶対、もっと正統で、もっとエレガントな方法があると思うんですが、今回は「実装よりも体験だ」って感じです。

とりあえず思いつく遊び方としては、

同時に二人しか使わなければ、遠方の友人と、telepathy 経由で五目並べができると思います。

面白いでしょうか ?

lib/stickerlib.min.js を調べてみて

今のところ、telepathy は、Puppet と Theatre を経由しないと使うの大変そうですね。でも、ま、今回 Theatre に割り込む方法が分かってきたので、一応収穫です。

私とおんなじくらい奇特な人は、今回の zip をダウンロードして中覗いてください。lib/stickerlib.min.js を整形したのとか、書き変えたのとかも入ってますので。

ではでは。

2015年1月11日 (日)

ページ間のコピペシール

特に新規性も、驚きもありませんが

ページをまたいでコピー&ペーストを行うシールを作りました。なんとなく今更な気持ちもしますが、ユーザーの立場で欲しくなったので。

使い方

元のページからストロークを clip するシールと、別のページでストロークを paste するシールの組み合わせで使います。

clipStroke シールを起動すると、クリップ範囲の指定を促す UI が表示されます。ペンで閉曲線を描いて、[Clip] ボタンをタップしてください。ストロークを clip して UI を終了します。clip できるのは、ページ台紙上のストロークのみで、ページ上に張り付けたシールのストロークや、背景画像は clip できませんので、あしからず。かなり遅いので、気長に待ってください。

Screenshot_20150111184808 Screenshot_20150111184836 Screenshot_20150111184904

clip したストロークは、noteStorage に保存されます。[Clear Clip] ボタンをタップすると、noteStorage の該当部分を削除します。

[Cancel] ボタンで、何もせずに終了。[Retry] ボタンをタップすると、一度描いた閉曲線を消去するので、clip 対象範囲を指定し直すことができます。

ストロークをクリップしたら、貼り付けたいページに移動します。

pasteStroke シールを起動すると張り付けるストロークが元と同じ位置に表示されます。最初に表示されるのは、ストロークそのものではなく画像なので、これをタッチでドラッグできます。自由に移動して好きな位置に paste することができます。適切な位置に移動したら、[Paste] ボタンをタップすれば、ストロークを台紙に保存して終了します。

Screenshot_20150111184955 Screenshot_20150111185034 Screenshot_20150111185055

[Cancel] ボタンをタップすると、paste せずに終了します。[Clear Clipped Stroke] ボタンをタップすると、noteStorage に保存されている情報を削除して終了します。

clipStroke シールか、pasteStroke シールで、明示的に保存されたストローク情報を削除しない限り、最後に clip した情報が noteStorage に残りますので、何度でも、paste を繰り返すことができます。一つのページに位置を変えて複数回 paste することも、複数のページに paste することも自由です。

しかし、残念なことに、pasteStroke シールは、clipStroke シール以上に動作が遅いです。初めて使うときは、止まってしまったのではないかと不安になるかもしれません。申し訳ありませんが、ここは諦めていただくしかありません。

あれこれ、感想とか、要望とか

潜在需要は以前からあったのですが、面倒で作っていませんでした。技術的にはこれまでのシールの切り張りで、あんまり新しいことはしていません。それでも、MOONPhase ver 3.0 で、enchant コマンドの割り当てができるようになったから作る気になったので、新機能を活用していると言えなくもないでしょう。

新機能と言えば、ver 2.10.0 で使えるようになった noteStorage を初めて使いました。単純な、クリップボード的な使用法です。実際には、折角のグローバルストレージなので、もっと夢のある活用法を考えたかったのですが、今のところ、「これだっ」というような使用法を思いつきません。なんとなく残念です。

今回のシール、理想を言えば、ストロークと背景とシール全体を含めてページを複製する機能があればよいのですが、このシールでも無いよりは幾分マシだと思います。

それでもやっぱり残念なのは、とにかく動作が遅い。特に、指定範囲からストロークを切り出す処理と、paste の際に位置を決めるためのドラッグする画像データの作成。前者は自分で書き起こしましたし、後者は MOON 発売当初に esmasui さんが作られた drawStroke.js をいまだにありがたく使わせてもらっています。しかし、javascript レベルで最適化を図っても限界があります。

考えてみると、この二つの処理は、ストロークを指で囲んでリンクシールを作る際の処理と同様です。システムレベルでは十分速く実行されます。この機能の API を是非公開して欲しいと感じています。或いは、canvas のサポート範囲を広げて、isPointinPath を非 javascript レベルで実装してくれるだけでも良いのですが。

まあ、あんまり地味なことばかり改修を続けていても埒が空かない部分もあるでしょうから、限られたマンパワーの中で、面白そうなこと優先で開発を続けてくれれば御の字でしょうか。まだ、バージョンアップの予定がある、というのは冷静に考えればスゴいことですしね。

2014年12月28日 (日)

ペン設定シールと、ストローク編集シールをアップデート

やっと時間ができたので

上記 2 シールを、enchant コマンド登録可能に修正しました。

対応方法は、前回の記事の通りです。

over head が問題になるほど頻用する手続きでもないので、関数をひとつ作ってその中に閉じ込めてしまいました。もし、流用したい人がいましたら、zip を展開した中の "miscUtil.js" の isAttached() 関数を参照ください。

今年の記事は、多分これで最終かな。だんだんアイディアが尽きてきてるので、来年はあんまり記事書けないかもしれないですが、あきらめずにもうしばらく遊んでいこうと思います。

ではでは。

2014年12月23日 (火)

古いシールを enchant コマンドに対応させる

enchant コマンド化できないシールがある !!!

MOONPhase を 3.0 に update しました。QR コードが使えたり、テレパシーとか、相当面白そうなんですが、どう使うか直ぐにはアイディアが出ません。正月の宿題になるのかも。

新機能の一つが、待望の "enchant コマンドの追加" です。ビバ、NO UI 。

喜び勇んで自作のシールを enchant コマンドに登録してみたのですが、動かないのがあるんですね。

最近よく使ってるのが、「ペン周りまとめて設定シール 」「NO UI っぽい編集シール」 なんですが(こんな感じ)、enchant コマンドに割り当てると、編集シールが動かない !!!

いじってると、なんとなくどこで引っかかってるのか想像がつくな、ってところで、検証用のコードを書いてみました。

検証用のコード

実動するシールは、[ここ]からダウンロードして下さい。hack.js だけの、簡単なシールです。


hack.js


importJS(["lib/MOON.js", "lib/enchant.js"], function() {

    enchant();

    function relDirectories( i ){
        var relURL = location.href;
        relURL = relURL.slice( relURL.search( /\/data\//i ) +1 );
        return relURL.split( "/" )[i];
    }

    var InfoUtil = {
        //MOONPhase v 2.8.0 での window.location に対応
        getStickerId: function(){ return relDirectories( 3 ); },
        getPageId: function(){ return relDirectories( 2 ); },
        getNoteId: function() { return relDirectories( 1 ); }
    };

    var sticker = Sticker.create();

    sticker.ondetach = function(event) {
        MOON.finish();
    };

    sticker.onattach = function(event) {
        MOON.finish();
    };

    sticker.ontap = function(event) {
        var id, info;

        id = InfoUtil.getStickerId();
        alert( id );
        try {
            info = MOON.getPaperJSON( id );
        } catch( e ) {
            alert( 'caught error' );
        }
        alert( info );
        MOON.finish();
    };

    sticker.register();
});

実験しましょう

このシールをダウンロードしてページに貼り付け、タップして実行すると、このような画面が順に表示されます。

Screenshot_20141223173504 Screenshot_20141223173517

これが従来の動作です。

次に、同じシールを enchant コマンドに割り当てて、enchant コマンドとして実行すると、以下のような画面遷移になります。

Screenshot_20141223173555 Screenshot_20141223173616 Screenshot_20141223173624

つまり、enchant コマンドとして実行すると、stickerId を取得できるので、ページの下に temporary にシール用の directory は作成されるものの、MOON.getPaperJSON() が失敗する、つまり、info.json は作成せずに実行されるようです。シールがページ上に貼り付けられないので、確かに info.json や strokeX.json は不要ですね。

error になるので、返り値は得られません。それまでに一度も代入されていない変数だと、値は undefined になります。

と、いうことで、よく使うシールを enchant コマンド対応に改修したいと思います。

対策はわかりました。(と思います)

既存のシールで、シール自体の info.json を参照しているコードがあれば、そこの error を捕まえる、あるいは戻り値が undefined であることをチェックして、その時はページ上にシールが張り付けられていないものとして動作させればよいわけです。

今週いっぱい働けば、年末休みに入りますので、今年中に改修してみたいですね。なにか追加の情報がつかめれば、またここで報告します。

2014年8月31日 (日)

ストローク編集シールに、「細字」コマンドを追加

ちょっとだけ更新

「ストローク編集シール」を使い始めてから気が付いたのですが、「太字」コマンドがあるのなら、ちょっと手を加えるだけで「細字」コマンドもできるじゃないですか。

さっさと実装しました。

「ストローク編集シール」をダウンロード

使い方

コマンド入力に受け付ける文字は、以下のどれかです。

"LIGHT", "ライト", "SLIM", "細字"

BOLD の反対語としては LIGHT が正しいのですが、手書きするのに面倒かと思って、"SLIM" も受け付けるようにしました。しかし、実際に使ってみると、ある程度画数の多い文字の方が認識しやすいようで、けっきょく "細字" で入力するのが使いやすいようです。

内容

ご想像の通り、対象とするストロークの太さを、それぞれ 1/2 にします。但し、線が消えてしまうといけないので、0.1 より細くはできません。

他にも何か簡単に追加できるコマンドありますかね?

2014年8月24日 (日)

遂に、コピペシールが update できました/v.2.10.0 の (?) storage 機能

「コピペシール」の update

去年作った「手書きコピペシール」 なのですが、MOONPhase v.2.9.0 に version up された時点で、info.json 周りの扱いと仕様が変わってしまって、v.2.9.0 対応できなくなってしまいました。

(過去記事:「peperJSON/strokeJSON の罠? 謎? 仕様? バグ?」 参照)

今回の v.2.10.0 への version up で、このあたりが地味に直ってます。おかげでとうとうコピペシールが update できました。

MOONPhase v.2.10.0 対応版「手書きコピペシール」をダウンロード

直った/直せたところ

まず、過去記事 に書いた、「MOON.getPaperJSON() の返り値に clip 属性が含まれていません」とという問題が直ってます。

また、「背景サイズが変えられない」と書きましたが、paperJSON.width, paperJSON.height に設定した値が正常に(!)反映されます。

.width, .height, .clip を変更することで、シールのサイズを変更することができるようになりました。

シール側のコードで直したのはこれくらいで、ほんの数行なのですが、システム側の改修は結構大変だったのではないかと思います。地味に前に進んでますね。

v.2.10.0 の (?) storage 機能

先週、普段使っていない Gmail を開いてみると、Issue tracker からの連絡/回答がいくつもきていました。お盆に結構返信があったんですよね。

で、そこにいくつか興味深い情報が。もしかすると v.2.9.0 から実装されていて、表に出てなかっただけなのかもしれませんが、今回 v.2.10.0 での MOONBlock の新機能と直接関係しているので、多分、v.2.10.0 の新機能なのだと思っています。

Issue tracker #148 の reply の中に、

確認また回答が遅くなり申し訳ございません。

ご要望頂いている任意の情報の保存について、
stickerStorage APIを使用する事でシールの情報の保持が可能となっています。

-----
保存:
stickerStorage.setItem("key", "value")

読込:
var value = stickerStorage.getItem("key")

保持している情報はシールのフォルダの
「storage.json」ファイルに保存されます。

という情報がありました。

また、Issue tracker #235 には

ご要望として頂いています、
「新しい属性のとして文字列の追加について」追記します。

info.jsonではなく、
stickerStorage, localStorage, pageStorageを使用する事で、
文字列属性の保存は対応可能となっています。

(e.g.
-----
stickerStorage.setItem("key", "value")
var value = stickerStorage.getItem("key")
-----

という情報があります。

試してみました。まず、"pageStorage" というのは "noteStorage" の間違いのようで

  • stickerStorage : そのシールからだけアクセスできる storage
  • localStroage : そのページの全シールからアクセスできる storage
  • noteStorage : そのノート全体の全ページの全シールからアクセスできる storage

のようです。

まだ使い道を思いつきませんが、面白そうです。ちなみに MOONBlock のヘルプ にある、ロードブロック、セーブブロック、セーブデータ確認ブロック、セーブデータ消去ブロック、がそのままこの機能を使っているのだと思われますから、直接 javascript を操らなくても利用できるはずです。

で、その実体としては、localStorage は、ページのフォルダの下に storage.json というファイルがあって、その中に情報が格納されています。

noteStorage は予想通りで、MyNotebook1 フォルダの下に、storage.json というファイルが作成されます。

今のところわからないのが、stickerStorage. Issue tracker の情報では、『シールのフォルダの「storage.json」ファイルに保存されます』とありますし、他の 2 つの storage.json の存在を考えると、これが一番自然なのですが・・・。

シールのフォルダには storage.json が見つからないんですよ !!!

それでも、stickerStorage.setItem(), stickerStorage.getItem() は正常に動作するので、使う分には問題ないんですが・・・。

この件、どうなってるのか分かった方がいれば、ぜひ教えてください。

MOONPhase v.2.10.0 対応

編集シールができて、MOONPhase も update されて喜んでいたのですが、「昔のシールが version check にひっかかって、2.10.0 で動かない」という報告を受けました。

なるほど確かに。いや、考えてみたら、当然なんですよ。version check は以下のコードで行っているのですが、これは version のそれぞれの桁が 2 桁になることを想定していません。まさか、ver.2.9.1 の次が ver.3.0.0 ではなくて、ver.2.10.0 とは思っていなかったので。

var isVersionSatisfactoryFor = function( requiredVer ){
    var message;
    switch ( MOON.getLocale() ) {
        case "ja-jp":
            message = 'MOONPhase ver.' + requiredVer + ' 以上に update しないと使用できません';
            break;
        case "en-us":
        default:
            message = 'To use this sticker, update to MOONPhase ver.' + requiredVer + ' or greater is required.';
            break;
    }

    if ( MOON.getSystemVersion() < requiredVer ) {
        MOON.alert( message, function(){ return false; } );
    } else {
        return true;
    }
};

安易にサボらずに、対応できるように書き直しました。

var isVersionSatisfactoryFor = function( requiredVer ){
    return true;
   /*
    * MOONPhase v.2.10.0 の release に伴い、
    * '2.10.0' > '2.9.1' が成立しなくなってしまったので、
    * 安直な文字列比較をあきらめて、数値比較にする。
    */
    function ver2array( verStr ){
        var verIntArray = [],
            strArray = verStr.split( '.' );
            for ( var i = 0, len = strArray.length; i < len; i++ ) {
                verIntArray.push( parseInt( strArray[i] ) );
            }
        return verIntArray;
    }

    function isLessThan( a, b ){
        for ( var i = 0, len = a.length; i < len; i++ ) {
            if ( a[i] >= b[i] ) return false;
        }
        return true;
    }

    var message;
    switch ( MOON.getLocale() ) {
        case "ja-jp":
            message = 'MOONPhase ver.' + requiredVer + ' 以上に update しないと使用できません';
            break;
        case "en-us":
        default:
            message = 'To use this sticker, update to MOONPhase ver.' + requiredVer + ' or greater is required.';
            break;
    }

//    if ( MOON.getSystemVersion() < requiredVer ) {
    if ( isLessThan( ver2array(MOON.getSystemVersion()), ver2array(requiredVer) ) ) {
        MOON.alert( message, function(){ return false; } );
    } else {
        return true;
    }
};

取り急ぎ、これで version check を回避できる。「手書き縮小貼り付けシール 」 を update しましたので、お困りの方は update してください。

追記:「定着シール 」も対応しました

2014年8月23日 (土)

作りたくて作ってみた - "NO UI" っぽい(?) 編集シール

javascript や enchant.js の使い方にも多少慣れてきて、最近いろんな UI のシールを作ってみてたのですが、「なんだか "NO UI" から離れていくなー。つまらんなー」と感じていました。

いくつかノウハウを蓄積してきて、改めて考えてみると、"NO UI" っぽいシールが作れそうです。完成しても、独りよがりで実は使い勝手の悪いものになる予感も感じながら、とりあえず、「作りたい」という理由で手を付けてみました。

できあがったもの: 「ストローク編集シール」をダウンロード

使い方

シールを起動させると、入力待ち状態になります。本当に "NO UI" だと、シールが起動したかどうかさえ分からないので、シールの位置で particle が回転・点滅する待ち受け表示を作りました。

Screenshot_20140823095219

入力待ちになったら、編集対象となるストロークを指定します。ペンで、画面にぐるりと囲み線を書いて下さい。少し反応が悪いかもしれませんが、通常のペン入力に近い感覚で線を描けます。

囲み線を書き終わると、ワンテンポ遅れて、コマンド入力欄が表示されます。ここに手書きで編集コマンドを書き込みます。

Screenshot_20140823094805

現在使える編集コマンドは以下のようになります。なお、アルファベットは大文字・小文字を区別せずに受け付けます。

  • "CANCEL" : ( 次の文字列でも受け付けます、以下同様。"キャンセル", "取り消し", "取消", "戻る" )
    • 囲み線入力 ( 編集対象指定 ) を取り消して、最初の待ち受け状態に戻ります。

  • "ERASE" : ( "消去", "消す" )
    • 対象とするストロークを消去します。

  • "COLOR" : ( "色" )
    • 対象とするストロークの色を変更します。

  • "BOLD" : ( "太字", "強調" )
    • 対象とするストロークの線を太くします。

  • "COPY" : ( "コピー" )
    • 対象とするストロークをコピーし、画面の任意の場所にペーストします。

  • "CUT" : ( "カット" )
    • 対象とするストロークをカットし、画面の任意の場所にペーストします。

各コマンドの編集操作の詳細は後述。

ひとつの編集操作を終えると、最初の入力待ち状態に戻ります。引き続き、繰り返して複数の編集操作を行うことができます。

このままだと永遠に入力待ちから抜けられず、終わりのない戦いになります。もちろん、ちゃんと終了する方法はあります。

入力待ち状態で、「回転・点滅待ち受け表示」の中心部をタップすると、システムメニューボタンが表示されます。

Screenshot_20140823095038

  • [EXIT] : シールを終了し、ストローク編集を確定させます

  • [Undo Last One] : 直前一回分の編集操作を取り消します。取り消しは一回分だけで、何回分も繰り返しさかのぼることはできません。

  • [Undo ALL] : シールを実行するまえの状態を保存してあるので、最初から最後までの編集操作を全て取り消すことができます。

各コマンドの編集操作 - 動作の単純な順に

ERASE : 対象のストロークが消えます、それだけ。

BOLD : 対象としたストロークが太くなります。

JSON 形式ストロークデータにおける "width" の値を倍にします。それぞれのストロークの width を倍にするので、対象としたストロークに太さの異なる線が含まれていると、それぞれ違う太さの太い線になります。何度も繰り返すとどんどん太くすることもできます。

Screenshot_20140823095244Screenshot_20140823095252 Screenshot_20140823095321 Screenshot_20140823095346Screenshot_20140823095359

COLOR : 対象としたストロークの色を変えます。

色選択ダイアログで選んだ色に、一括変換します。

Screenshot_20140823095101Screenshot_20140823095210Screenshot_20140823095219_2

CUT/COPY : 対象元のストロークを消去してから操作するか、消去せずに残して操作するか、の違いだけで、基本的には同じように操作します。

コマンド入力欄に CUT, COPY などと記入したあと、ワンテンポ遅れてコマンド入力欄が消えます。この時点で、囲み線で囲んだ範囲のストロークを指でドラッグできるようになります。適切な位置にドラッグできたら、[Paste] ボタンをタップして貼り付けです。張り付けてもすぐ入力待ちモードには復帰しませんので、別の場所にドラッグして、あちこちなんども張り付けることができます。[Cancel to the Top] ボタンをタップすると、最初の入力待ち状態に復帰します。

Screenshot_20140823095428Screenshot_20140823095447

CUT/COPY 編集操作中に、対象ストロークをドラッグせずに、タップだけすると、操作ボタンが [< EXPAAND >] と [> Shrink <] に変わります。またタップすると、[Paste], [Cancel to the Top] に戻ります。

Screenshot_20140823095511

[< EXPAAND >], [> Shrink <] をタップすると操作対象ストロークを拡大/縮小できます。最少でオリジナルの 0.1 倍、最大で 2 倍です。0.1 倍刻みで拡大/縮小します。このボタンを活用すると、サイズを変えてコピペできます。拡大の方はどれほど使うか分かりませんが、縮小を使うと、手書きの難しい小さなサイズの文字を張り込むことができます。

Screenshot_20140823095525Screenshot_20140823095539

弱点

作る前は、どの処理が律速になるのかなと思ってましたが、そもそもの、操作対象ストロークをページデータから切り出す作業が一番重いようです。ページ上の全ストロークデータを一本一本、囲み線の内部か外部か判定するので、ページにストロークが少ないと軽いのですが、たくさん書き込んだページだとすぐ重くなります。MOONPhase ver.2.8.0 以前を思い出しますね。

雑感

去年の夏から MOON に触れ始め、まともに使ったことのなかった javascript を使い始め、最初につくったシールをこのブログに上げた日付が 2013/8/31 でした。それから約一年、さすがに少しは凝ったものも作れるようになったな、という思いと、それでも所詮この程度のパズル規模のものしか書けないな、という限界を感じます。

OS の version up で、また作りたいものが増えていけば楽しいですし、もし今後 mRuby も載ってくるようなら、もともと ruby の方が好きなので、それもそれで楽しみです。

まだ暫くは遊べそうですね。

さて、MOONPhase ver.2.10.0 に update しなければ。

2014年7月30日 (水)

欲しくて作った - 「ペン周りまとめて設定シール 」

欲しくて作ったシリーズ - 「ペン周りまとめて設定シール」

MOON で、ちょっとまともにメモを取ろうとすると、ペンの太さを変えたり、flat ペンと、normal ペンを切り替えたりしたくなってきます。shi3z 社長謹製の手書きで太さを変えるシールとか、他の方が作ったペン色変更シールもありますし、MOONBlock でシールを作ればなんとでも設定できるのですが、MOONBlock で作ったシールだと、1 設定 1 シールで、あれこれ設定の組み合わせを変えるには複数のシールを使いまわさないといけないのも負担です。また、そもそも、シールを選んで張り付けてから、改めてタップするのが(私には)面倒です。どうも、貼ったらすぐ動いてくれるシールが欲しいんですね、私。

ということで、欲しかったので作りました。お絵描きするわけではないので、ペン色の変更は必ずしも必要ないのですが、せっかくなのでペン色も変更できる、複数の設定を interactive にまとめて設定できるシールを作りました。

Download : 「ペン周りまとめて設定シール」

尚、色選択の場面で表示される、「ペン色パレット-HEXタイプ」は、オリジナル作者の @inaber_gadget さんの許可をいただいた上で、再利用しやすいように改造して組み込ませていただきました。この場を借りてお礼申し上げます。おかげでカッコいい画面になりました。

使い方

まずシールを貼ると、その場ですぐ起動します。貼りつけてからタップするまで私が待てないからです(^^;

Screenshot_20140730215942

「実行」に関するボタンが、画面上下に 3 つあります。一番上のボタンは実行せずに終了します。入力内容を設定に反映せずにキャンセルしたい場合、或いは、シールを起動したれど、すぐに設定変更する必要はなく、シールを貼るだけでよい場合に使います。(後者だと、結局素直にシール台帳から貼りつけた場合と同じことになります。人によっては却って2度手間だと感じるかも)

画面下の 2 つのボタンはどちらも入力内容を設定に反映させます。違いは、左下のものだと設定反映を実行した上で、シールを直ぐ剥がしてしまうのに対し、右したのシールだと、実行・終了してもシールがページ上に残ることです。一度設定すればよい場合は左下、ページに書き込む間に何度か設定を切り替えて使いたい場合は右下、という風に使い分けて下さい。

flat pen モードと、ペンポインタの表示・非表示の切り替えは check box になっています。タップで切り替えます。

ペン色の選択は radio button になっています。どれか一つをタップすると、選択が切り替わります。普通、radio button は丸にするものですが、今の MOON の Canvas に丸を描くときれいにならないので、とりあえず四角にしています。将来的には丸に直したいです。
check box も radio button も指でタップすればよいのですが、ちょっと細かいので、ペン先でのタッチも効くように作ってあります。

Screenshot_20140730220040

色選択の radio button では、'other' を選ぶと、右端の色タイル部分の縞模様が消えます。縞模様のない状態で色タイル部分をタップすると、@inaber_gadget さんからお借りした色選択画面が開いて画面タップで色を選べます。この画面でも、ペン先でのタッチが有効です。色を選んで [OK] ボタンをタップすれば決定です。ここで選んだ色が、各モードのペン色に設定されます。

Screenshot_20140730220125Screenshot_20140730220131

ペンの太さを入力する部分が 2 箇所あります。現在の画面での太さと、デフォルトペンの太さです。

解説部分で後述しますが、ここはちょっと工夫して作った部分です。ペンの手書きで入力できます。好きな字を書いて、暫く入力しないでいると、フォントに変換されます。

Screenshot_20140730220444Screenshot_20140730220541

少々残念なのが、現在の MOONPhase の API だと、手書き入力の文字認識の際に、文字の種類を限定することができず、数字のみとして解釈、とか、アルファベットのみとして解釈、といった動作が実現できません。そのため、数値を入力したいのに、結構文字として誤認識されてしまいます。ここはひとつご容赦下さい。

Screenshot_20140730220556

誤認識されたら、正しく認識されるまで、諦めずに何度も入力して下さい。(^^;
フォントの上から手書きすれば、入力が更新されます。

フォントが表示されたままだと手書き入力しにくい、という場合は、一旦表示を消去できます。手書き入力領域いっぱいに、横棒をび~っと引いて下さい。領域がからっぽになります。

Screenshot_20140730221521Screenshot_20140730220753

この消去動作には少しだけ解説が必要です。本当は、入力領域の左外側から右外側まで、またぐ形で横棒を引いて消去、という UI にしたかったのです。しかし、入力領域の外側から引き始める線のイベントを拾うことができませんでした。妥協案として、入力領域の左側 1/4 から右端まで、或いは右側 1/4 から左端まで(左利きだとこうなりますよね)一本横棒を引くと、消去動作とみなすようにしました。

Screenshot_20140730221733

シールの起動時には、現在の設定内容を読み込んで、それを画面表示に反映してあります。上記のような操作で、変更したい処を操作して下さい。実行ボタンをタップすると(ここはペンでのタッチを殺してあります)起動時から変更されている部分のみ設定されます。

設定条件の依存関係として、白背景あるいは黒背景のデフォルトペン色、デフォルトペンの太さを変更すると、(それが現在の画面の背景色と同じなら)現在のペン色、ペンの太さも自動的に同じ値に設定されます。一方、現在のペン色、ペンの太さを変更しても、デフォルトペンの設定には影響しません。

自分で作ってみてなんですが、そこそこ使いやすいと思います。

解説 & 言い訳

色選択画面は、 Skylabβ から @inaber_gadget さんの「ペン色パレット-HEXタイプ」を download して改造しました。(参考ページ)

方法は、正に、以前の記事 - 「MOON で使い回しの効く GUI の作り方?」 で書いたまんまです。core.onload = function(){}; の中から、scene 単位で切り抜いて、interface を整えただけです。先に考察済ませておいて正解でした。

今回、このプログラムの為だけに lib/MOONwidget.enchant.js を書きました。はっきり言って「車輪の再発明」なことこの上ないです。しかも、このプログラムでしか使ってないので、interface 周りの汎用性が全然ありません。あちこち使いまわして鍛えれば、いつかは汎用ライブラリに出世できるかもしれませんが。

でも、check box や radio button はともかく、手書き入力できる MW_PenInputBox はちょっといいのができたなと思っています。Sprite と同じように scene に貼りつけるだけで手書き入力できるし、'change' event を受け取ったら value プロパティで認識済み文字列を引き出すことができます。横に一本取り消し線を描いてフォントを消去という UI も是非実装したかったので、まあまあうまくいって納得です。

これで、MOON.recognizeStrokes() が、MOON. penPrompt() みたいに入力文字の種類を限定できるようならもっと有用性が上がるのですが。MOONPhase ver.3.0.0 以降で API を提供して欲しいですね。

手を出さなかったこと

ここまで作ったので、デフォルトの背景色変更機能もつけようかな、と考えたのですが、

  • そろそろ画面が狭くなってきた
  • 背景色が「白」「黒」だけなら簡単なのですが、それ以外の色を設定するときに、デフォルトのペン色などをどうするべきか、考えがまとまらなかった

などの理由で手を出していません。自分で使っていて、こういう機能が欲しくなってきたら実装するでしょう。

作ってみてわかったこと

面白いことに、「現在のペンの太さ」には、小数点以下まで設定できるのに、「白・黒拝啓の時のデフォルトペンの太さ」は整数値しか設定できないことが分かりました。デフォルトペンに少数点以下までの数値を設定してシールを実行し、もう一度シールを起動すると、設定されている値を読み込んで画面に表示するのですが、きれいに値が切り捨てられています。何か意味があるんでしょうかね。

2014年6月28日 (土)

欲しくて作った - 「Evernote すぐアップシール 」

欲しくて作った第二弾

前回以上に安直ですが、個人的に欲しかったので作りました。(というより、パクったに近い)

Skylabβができたので、初期リリースからプリインストールされている Evernote アップロードシールの需要はなくなっているのかもしれませんが、個人的には捨てがたいものがあります。

将来の夢は別として、今現在 MOON をメモ端末として使うなら、メモの出口の一つとして有用です。しかし、プリインストール版には、以下の不満があり今一つでした。

  • 気軽にアップしたいのに、「貼って」「タップして」「剥がす」というのが面倒。
  • アップロードしたイメージにシールの画像が含まれてしまうこと。

この二つだけ改善すれば気軽に多用できそうなので、ブリインのシールを改造(不要部分を削除したくらい、改造のうちにも入らない)しました。

ダウンロード : 「Evernote すぐアップシール 」

使い方

そのままです。

アップロードしたいページにシールを貼りつけると、すぐにアップロードします。プリインシールの派手なエフェクトは省略しました。アップロードに成功すると自動でシールを剥がして終了します。

それだけ。

一つだけ難点なのは、三本指キャンセルすると、表示されない(透明の)シールがページ上に残ってしまうこと。申し訳ありませんが、手動で剥がしてください。

与太話

ほとんど、キュー○ー 3 分ハッキングレベルの改造ですが、それでも思うところはありました。

まず、ver.2.8.0 までで使えた、シールを剥がしてから、ondetach でイベントを catch して実行するテクニック (以前、Issue Tracker で教えてもらったやつ) が使えなくなってるみたいです。

シールを手動で剥がすと、ondetach で catch できるのですが、プログラム中から MOON.peel() で剥がすと、catch できません。MOONPhase ver.2.9.0 以降、イベントの伝播が変わってしまったのかもしれません。このギミックが生きていれば、途中で実行をキャンセルしても透明シールがページに残らずに済むのですが。

また、将来のバージョンで実装してほしいのですが、MOON.alert() が "OK" しか返せないのが残念です。同じスタイルで、[YES], [NO] とか、[Cancel] まで返せるような、複数選択肢のあるダイアログとして使える API を用意して欲しいですね。

ソース

今回はあまりにもソースが短いので、久々に載せておきます。この程度のもんです。

hack.js

importJS(["lib/stickerlib.min.js", "lib/infoutil.js", "main.js"], function() {
    enchant();

    var sticker = Sticker.create();

    sticker.ondetach = function(event) {
        MOON.finish();
    };

    sticker.onattach = function(event) {
        main();
//        MOON.finish();
    };

    sticker.ontap = function(event) {
        main();
//        MOON.finish();
    };

    sticker.register();
});

/*
*  onattch, ontap で MOON.peel() を実行し、ondetach で
*  シールが剥がれたことを catch して Evernote への upload を実行すれば
*  途中で実行を中止しても、シールがページに残らないのでスマートだが
*  MOONPhase ver.2.8.0 までで有効だった、このテクニックが ver.2.9.0 以降は
*  無効になっている?
*  シールを手動で剥がした場合は、ondetach で catch できるが、プログラム中から
*  MOON.peel() で剥がすと、ondetach にイベントが伝わらない ?
*/

main.js

function main() {
    var stickerId = InfoUtil.getStickerId(),
        stickerInfo = MOON.getPaperJSON( stickerId );

    /*
    *  シールが Evernote に残るのが不細工なので、シールの背景画像を
    *  消去してから upload 作業に入る。
    *  但し、シールの実行を三本指キャンセルで停止させると、
    *  透明のシールが剥がされずに残ってしまう
    */
    stickerInfo.image = "";
    MOON.setPaperJSON( stickerId, stickerInfo );

    /*
    *  uploadCurrentPageToEvernote() 自体が confirm message を出力するので、
    *  シール独自での confirm message は出さない
    */
    uploadEvernote( function( result ){
        if ( result ) {
            MOON.peel();
        } else {
            stickerInfo.image = "background.png";
            MOON.setPaperJSON( stickerId, stickerInfo );
            MOON.finish();
        }
    });
}

function uploadEffect() {
}

function uploadEvernote( callback ) {
    var _callback = callback;
    var UPLOAD_SUCCESS_MESSAGE, UPLOAD_FAILURE_MESSAGE;
    switch (navigator.language) {
    case 'en-us':
        UPLOAD_SUCCESS_MESSAGE = 'Successfully stored the page\n' + 'in your Evernote account.';
        UPLOAD_FAILURE_MESSAGE = 'Failed to store the page\n' + 'in your Evernote account.';
        break;
    case 'ja-jp':
        /* falls through */
    default:
        UPLOAD_SUCCESS_MESSAGE = 'Evernoteに保存しました';
        UPLOAD_FAILURE_MESSAGE = 'Evernoteへの\n' + 'アップロードに失敗しました';
        break;
    }
    MOON.uploadCurrentPageToEvernote(function() {
        MOON.alert(UPLOAD_SUCCESS_MESSAGE, function() {
            uploadEffect();
            _callback( true );
        });
    }, function() {
        MOON.alert(UPLOAD_FAILURE_MESSAGE, function() { _callback( false ); });

    });
}

lib/infoutil.js

/*
*  InfoUtil
*   by @H_Kuruno - http://kuruno.cocolog-nifty.com/blog/
*/
(function(global) {
    function relDirectories( i ){
        var relURL = location.href;
        relURL = relURL.slice( relURL.search( /\/data\//i ) +1 );
        return relURL.split( "/" )[i];
    }

    var Me = {

        //MOONPhase v 2.8.0 での window.location に対応
        getStickerId: function(){ return relDirectories( 3 ); },
        getPageId: function(){ return relDirectories( 2 ); },
        getNoteId: function() { return relDirectories( 1 ); },

        // JSON から、tag で指定した値を読み込む。
        // tag が存在しなかった場合の戻り値を指定できる。
        //
        getObjFromJSON: function( json, tag, nullDefault ) {
            var obj = json[ tag ];
            if ( nullDefault == null ) {
                return obj;
            } else {
                return ( obj == null ) ? nullDefault : obj;
            }
        },
        // JSON に、値 obj を 名 tag で登録する。
        // 但し、値が null, {}, [] の場合は削除する。
        //
        setObjToJSON: function( json, tag, obj ) {
            if ( (obj == null)
              || ( (typeof obj).match( /object/i ) && Object.keys( obj ).length <= 0 )
            ) {
                if ( json[ tag ] ) delete json[ tag ];
            } else {
                    json[ tag ] = obj;
            }
            return json;
        },
        /*
        * MOONPhase ver.2.9.0 以降の世代別 JSON における、全ストローク消去処理
        *
        *  backingId : paperJSON.backing を渡すとページのストロークを、stickerId
        *   を渡すと、シールのストロークを消去する。
        *
        *  戻り値 : 消去前の externalStrokes
        *
        *                                       last modified on 2014/06/13
        */
        clearStrokes: function( backingId ){
            var nullExtStrokes = [],
                extStrokes = MOON.getStrokeJSON( backingId );
            for ( var i = 0, eLen = extStrokes.length; i < eLen; i++ ) {
                nullExtStrokes.push( {strokes:[]} );
            }
            MOON.setStrokeJSON( backingId, nullExtStrokes );
            return extStrokes;
        },
        /*
        * ページにストロークが一本もない場合、とりあえずページ全体のサイズで
        * ストローク情報を作っておく. (for MOONPhase ver.2.9.0 or later)
        *
        *                                       last modified on 2014/06/13
        */
        confirmExtStrokesNotEmpty: function( backingId ) {
            var backingInfo = MOON.getPaperJSON( backingId );
            if ( backingInfo.externalStrokes.length === 0 ) {
                backingInfo.externalStrokes = [
                    { x: 0, y: 0, width: backingInfo.width,
                    height: backingInfo.height, size: 0, name: 'stroke1.json' }
                ];
                MOON.setPaperJSON( backingId, backingInfo );
            }
        }
    };

    global.InfoUtil = Me;
})(this);


/*
*  StorageUtil
*   by @H_Kuruno - http://kuruno.cocolog-nifty.com/blog/
*/
(function(global) {
    var Me = {
        // localStorage から、tag で指定した値を読み込む。
        // tag が存在しなかった場合の戻り値を指定できる。
        //
        get: function( tag, nullDefault ) {
            var obj = localStorage[ tag ];
            if ( nullDefault == null ) {
                return obj;
            } else {
                return ( obj == null ) ? nullDefault : obj;
            }
        },
        // localStorage に、値 obj を 名 tag で登録する。
        // 但し、値が null, {}, [] の場合は削除する。
        //
        set: function( tag, obj ) {
            if ( (obj == null)
              || ( (typeof obj).match( /object/i ) && Object.keys( obj ).length <= 0 )
            ) {
                if ( localStorage[ tag ] ) localStorage.removeItem( tag );
            } else {
                localStorage[ tag ] = obj;
            }
        }
    };

    global.StorageUtil = Me;
})(this);

より以前の記事一覧

その他のカテゴリー