enchantMOONのストロークを背景画像にするシールをつくった

概要

enchantMOONのページのストロークを背景画像に設定するシールを作りました。

  • シールのダウンロード

background.moon.zip

使い方

シールを実行すると現在のページのストロークを、そのページの背景に設定します。オリジナルのストロークは削除しないので、オリジナルのストロークが不要な場合は消しゴムなどで消してください。

なお、背景に設定される画像はオリジナルのレンダリングよりも劣化するのご注意ください。これはシールで使っているラスタライザ(enchantMOONのストロークをラスタライズするライブラリをつくった)の品質の問題です。

使いみち

罫線シールなどで背景のように振る舞うストローク引いてしまうと、ノート検索にヒットしなくなるようなので、そういう場合にシールで引いた罫線を背景画像化するなどの用途に使うとよいと思われます。

しかし、そのような場合はそもそも罫線をひいた背景画像を設定するような罫線シールをつくったほうがよいかもしれません...
うーん。

解説

ストロークJSONレンダリングしたcanvasを引数にして、MOON.saveImage(string, HTMLCanvasElement)を呼び出して画像を生成・保存しています。

var page = MOON.getCurrentPage(),
    bg = "background.png"
    w = 768,
    h = 1024,
    canvas = ...

MOON.saveImage(page.backing + "/" + bg, canvas);
backing["transparent"] = true;
backing["image"] = bg;
backing["clip"] = {"width":1.0,"color":0,"type":"pen","data":[0.0,0.0,1.0, w,0.0,1.0, w,h,1.0, 0.0,h,1.0]};
MOON.setPaperJSON(page.backing, backing);

enchantMOONのストロークをラスタライズするライブラリをつくった

概要

enchantMOONストロークを操作するシールを作ろうとすると困ることがあります。それはレンダリングされたストロークを取得する手段がないことです。例えば、ページに描いた絵をアニメーションさせたり、範囲選択したストロークをドラッグさせたり、といったことを行うにはラスタライズされたストロークが必要になります。現状、enchantMOONにはそのようなAPIがありません。じゃあつくるか...、できたよ...というお話。


enchantmoonstrokes的なデモ


ストロークをアニメーションさせるデモ


画面が赤っぽいのは、ぼくんちの蛍光灯が白熱灯色だからですね。どうでもいいですね。

ストロークをアニメーションさせるデモで使ってるシール

  1. 下記リンクからシールダウンロードする
  2. シールを適当なページ(ブランクのページがよい)に貼り付ける
  3. あまり大きくない、アニメーションさせたい絵を描く
  4. 動かしたい軌跡を一筆で描く(最後に描いたものが軌跡として認識されます)
  5. シールをタップする
  6. シールが軌跡に沿って動いたあと、絵は軌跡の最後の位置に移動し、軌跡のストロークが除去されます。


>>ストロークをアニメーションさせるデモで使っているシール

enchantMOONのシミュレータのようなものをつくった

概要

HTML5enchantMOONのシミュレータのようなもの( enchantMOON/simulator)をつくりました。


主にMOON.getPaperJSON(), setPaperJSON()を利用するシールのデバッグにおいて有用です。また、そのとおりにMOON.setPaperJSON(), MOON.getPaperJSON()が動けばいいやくらいの感じでしか作っていないので、そのくらいの機能しかありません。
 ページのレンダリングには、過去エントリ「enchantMOONのストロークをTwitterに投稿するシールを公開しました」で作ったものを使っていますので、レンダリング結果はenchantMOON本体と差異がある場合があります。

動作確認はMac版のChromeでしか行っていないので、それ以外の環境では動作しない可能性があります。

サポートしているMOON.jsの機能

サポートしているMOON.jsの機能は以下のとおりです。
◯がついているものは概ねenchantMOONの実機のとおりに動作します。×がついているものは、呼び出しても何も起こりません。

API サポート 備考
loadData -
alert 見栄えがMOONっぽくない
penPrompt ペン入力じゃないpromptが出る
getCurrentPage -
setCurrentPage × -
getImagePath × -
searchWeb -
getPaperJSON -
setPaperJSON -
openStickerPage × -
openNotebook × -
uploadCurrentPageToEvernote × -
getPageThumbnail × -
getEditPaperThumbnail × -
peel × -
finish シールから作ったcanvasを消すだけ
openUrl -
openPage × -
setPenColor × -
setPenWidth × -
searchStorage × -
showParticle × -
showParticles × -
recognizeStrokes × -

使い方

ストレージをマウントしてシールのディレクトリにHTMLファイルを一つ置くだけです。

  1. enchantMOONのストレージをPCにマウントする
  2. デバッグしたいシールのディレクトリに enchantMOON/simulator/index.html at master · esmasui/enchantMOONをコピーする
  3. ローカルファイルにアクセスできるように起動したブラウザ(後述の「ブラウザの設定」を参照)で index.html を開く
  4. シールをタップするとontapのイベントハンドラが実行される
    1. onattach, ondetachの実行は未実装。 window.__sticker__ にシールのオブジェクトが格納されているので Webコンソールでonattach(), ondetach()を呼び出すなどしてください。
  5. hack.jsを編集してリロードしたり、consoleでデバッグしたりする

index.html をコピーする場所

index.htmlはシールのディレクトリにコピーします。以下のようにシールのディレクトリが "rQDI2Kk61378488759161"だとすると、index.htmlはそのディレクトリにコピーします。

MyNotebook1

  • 5zBI2Dod1378452446175
    • hDQaxgxg1378452446175
    • rQDI2Kk61378488759161

ブラウザの設定

index.htmlを読み込むブラウザはローカルファイルにXHRできるようにして起動してください。

Mac版のChromeでは以下のコマンドでChromeを起動します。

$ open -a Google\ Chrome --args --disable-web-security

他のOS、ブラウザでの方法はここでは説明しませんので各自の環境にあわせた方法で行ってください。

enchantmoonstrokesの再生速度とかの変え方

概要

enchantmoonstrokesの再生速度とかを変える方法を説明します。パラメータを指定するUIはつくっていないのでクエリ文字列で指定します。


 例示のURLは[twitter:@abfly]さんの投稿を使わせていただきました。

fps

一秒間に描画するフレーム数を指定します。ただし描画が間に合わない際に動体のように中途を省くわけにもいかないので、これより高くなることはないというだけの指定です。
デフォルト値は60です。

http://enchantmoonstrokes.appspot.com/?fps=10#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

unit

1フレームで描画する辺の数を指定します。
デフォルト値は120です。

http://enchantmoonstrokes.appspot.com/?unit=512#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

resolution

線を描画する際の分解能を指定します。数字を低くするほど細かくなります。
デフォルト値は0.5です。

http://enchantmoonstrokes.appspot.com/?resolution=5#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

minimumAlpha

筆圧によって変化する、線の透明度の最低値を指定します。
デフォルト値は0.1です。

http://enchantmoonstrokes.appspot.com/?minimumAlpha=0.5#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

minimumWidth

筆圧によって変化する、線の太さの最低値を指定します。
デフォルト値は0.5です。

http://enchantmoonstrokes.appspot.com/?minimumWidth=0.1#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

easing

筆圧のかかり方を変化させる関数を指定します。筆圧のかかり方が変わると描画される線の強弱が変化します。
デフォルトはlinear(変化させない)です。

以下のいずれかを指定可能です。

  • linear
  • swing
  • easeInQuad
  • easeOutQuad
  • easeInOutQuad
  • easeInCubic
  • easeOutCubic
  • easeInOutCubic
  • easeInQuart
  • easeOutQuart
  • easeInOutQuart
  • easeInQuint
  • easeOutQuint
  • easeInOutQuint
  • easeInSine
  • easeOutSine
  • easeInOutSine
  • easeInExpo
  • easeOutExpo
  • easeInOutExpo
  • easeInCirc
  • easeOutCirc
  • easeInOutCirc
  • easeInElastic
  • easeOutElastic
  • easeInOutElastic
  • easeInBack
  • easeOutBack
  • easeInOutBack
  • easeInBounce
  • easeOutBounce
  • easeInOutBounce

http://enchantmoonstrokes.appspot.com/?easing=easeOutQuad#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

debug

デバッグ出力を指定します。現状はレンダリングの範囲を矩形で表示しています。
デフォルト値は0(出力しない)です。1にするとデバッグ出力を行います。

http://enchantmoonstrokes.appspot.com/?debug=1#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

複数指定する場合

パラメータを複数指定する場合は "&" で連結します。以下はfptとunitを同時に指定する場合の例です。

http://enchantmoonstrokes.appspot.com/?fps=10&unit=240#0b705473f0b128690de3c0ee0f8690d40ed134f1008b160b9

enchantMOON - MOONBlockまにあっくす

概要

MOONBlockの実装を調べてみよう、みたいな話。MOONBlockの使い方等は一切でてきません。

 MOONBlockはenchantMOONにバンドルされたビジュアルプログラミング環境です。enchantMOONで作成したシールはコンテキストメニューの「Hack」を選択することでMOONBlockで編集できます。編集できるということは、シールのデスクリプタである manifest.json、メインプログラムである hack.js の読み書きを何らかの方法で実現しているということです。そこで、MOONBlockがどうやってそれらのファイルを読み書きしているのか調べてみました。

MOONBlockの構成

MOONBlockのプログラム本体は、/App/MOONBlock/main.js です。main.jsは多く分けて四つの要素で構成されています。

  1. ユーザーインターフェイス
  2. Block
  3. Manager
    • Blockを管理するManagerクラス
  4. Editor
    • Managerクラスとインタラクションしてファイルの読み書きを行うEditorクラス
ユーザーインターフェイス

ユーザーインターフェイスは「Code」、「Done」などのコマンドの制御、画面へのBlockの配置と操作など受け持ちます。

Block

Blockは自身に設定可能なプロパティの定義と、Blockが実際に実行される際のJavaScriptでの表現の生成を受け持ちます。
ただし、これらは main.js ではなく別のファイルに定義されています。

Manager

ManagerはBlockを管理し、manifest.json、hack.js に相当するコンテンツの符号化と復号を行います。Managerが責務を持つのはあくまでコンテンツの内容までで、それらを実際のファイルに対して読み書きすることはありません。

 Managerは enchant.block.Manager として block.enchant.js に定義されています。

Editor

Editorこそが、MOONBlockでファイルの読み書きを担当するクラスです。EditorはManagerとインタラクションしてコンテンツを実際のファイルに対して読み書きします。以降、このEditorを掘り下げて見ていきます。

Editor

いきなりですが、前項のEditorの説明は若干正確性を欠いています。Editorがファイルの読み書きを行うかのように説明しましたが実際にはそうではありません。Editorはいわゆる抽象クラスとして存在しており、その定義は以下のようになっています。

function Editor(name, version) {
        /**
         * It will be called when save script text.
         * @type {Function}
         */
        this.saveText = null;
        /**
         * It will be called when save editor state.
         * @type {Function}
         */
        this.saveState = null;
        /**
         * It will be called when load script text.
         * @type {Function}
         */
        this.loadText = null;
        /**
         * It will be called when load editor state.
         * @type {Function}
         */
        this.loadState = null;
        /**
         * @type {Object}
         * @private
         */
        this._dependencies = {};
        /**
         * @type {Manifest}
         */
        this.manifest = new Manifest({
            name: name,
            version: version
        });
    } 


このEditorはシールを実行しているコンテキストでは存在せず、MOONBlockを実行しているコンテキストでのみ存在しています。そして、定義を見ればわかるように、以下のメソッドは実体が定義されていません。つまり抽象メソッドになっています。

  • loadText
  • saveText
  • loadState
  • saveState


main.js でこれらメソッドをオーバーライドしている箇所を見てみると、それぞれのメソッドのシグネチャがわかります。シグネチャ擬似コードで見てみましょう。

function loadText(str:string):null

function saveText(): string

function loadState(jsonString:string):null

function saveState(): string


メソッドはtext、 stateという対象にそれぞれload, saveという操作を指示するようになっています。text がメインプログラムである hack.js、state がデスクリプタである manifest.json のコンテンツにあたります。

 これらメソッドはMOONBlockの実行時に以下のタイミングで呼び出されます。

  • MOONBlockの起動時、hack.jsのコンテンツを引数にして loadText(str) が呼ばれる
  • MOONBlockの起動時、loadText(str)の後にmanifest.jsonのコンテンツを引数にして loadState(jsonString) が呼ばれる
  • 「Code」ボタンが押された時、ダイアログに表示するJavaScriptのコードを生成するために saveText() が呼ばれる
  • 「Done」ボタンが押された時、hack.js に保存するコンテンツを生成するために saveText() が呼ばれる
  1. 「Done」ボタンが押された時、manifest.json に保存するコンテンツを生成するために saveState() が呼ばれる

結論

というわけで、MOONBlockのファイル読み書きの仕組みが理解できました。残念ながら(?)直接的なファイルIOを行っている箇所はありませんでしたが、これらの仕組みを理解することは、alternativeなオレオレMOONBlockを作る際に有用でしょう。

__moon__.invokeの謎

MOONBlockの「Done」ボタンを押すと、Editor.finishEdit というメソッドが呼ばれてMOONBLockが終了します。このメソッドの定義は以下のようになっています。

function() {
    __moon__.invoke('finishEdit', '1', '[]');
}


これをMOONBlockの実行中に呼び出すと、MOONBlockは終了します。しかし、シールの実行中にこのステートメントを実行すると "NotSupported" というエラーが返ります。このエラーは __moon__.invoke() でサポートされていないメソッド名を指定したときに発生するエラーです。

 __moon__ はコンテキスト非依存なグローバルオブジェクトだと思っていましたが、そうでもないようです。或いは実際は呼ばれていて、環境を判断してエラーを発生させているのかもしれませんが...

enchantMOONで任意のURLをブラウザで開くシールのつくりかた

概要

任意のURLをブラウザで開くシールのつくりかたを説明します。

enchantMOONのブラウザにはアドレスバーがないので、特定のURLを開きたいときにやや不便。MOONBlockで「ハイパーリンク」ブロックを使えばできるけども、開きたいURLができるたびにMOONBlockを開くのはちょっと面倒...。とくに、ダウンロードしたいシールを探すときとか...。 そんな時に作っておくと便利なシールの作り方を説明します。

つくりかた

  1. 任意のシールをHackしてMOONBlockを開く
  2. JavaScriptの「実行」ブロックをシールの「タップされたとき」にドロップする
  3. 「実行」ブロックをタップするとプロンプトが開くので、以下のとおり入力する(大文字小文字を区別するので注意)
  4. 「Done」をタップしてMOONBlockを終了する


「実行」に入力する内容

MOON.openUrl(window.prompt())


シールの全体

使い方

以下の通りにすると指定したURLでブラウザが開きます

  1. つくったシールをタップする
  2. プロンプトが開くので、任意のURLを入力して「OK」を押す


指定したURLがenchantMOONのシールのzipファイルだった場合は、ダウンロードが始まります。
その際、ダウンロード完了後に台帳後から戻ってくると黒い画面が表示された状態になっているので、三本指スワイプでキャンセルしてノートに戻ってください。

http:// って毎回打つのめんどくせぇ!という方は


こんな風にすれば、任意の文字を初期値として指定できます。

MOON.openUrl(window.prompt("", "http://"))


プロンプトのタイトルが消えちゃって気持ちわるいという方は、さらにこんな風にすれば好きなタイトルを指定できます。

MOON.openUrl(window.prompt("てきとうなたいとる", "http://"))


別に初期値は要らんけど、タイトルは指定したいというひとはこんな感じです。ちなみに、タイトルはプロンプトに表示されるだけのタイトルであって開くURLとは無関係だし、それだけの代物です。

MOON.openUrl(window.prompt("いかしてるたいとる"))


以上でおわかりのようにwindow.prompt()は、タイトル、初期値の順でオプションを指定できるようになっています。

プロンプトの外観について

開いたプロンプトの見栄えがAndroid(しかも有史以前に存在していたという噂のバージョン風のレトロなやつ)すぎるわけなんですが、これは現在、開発者の方がMOONっぽい見栄えになるように作業してくれています。できるだけ早くアップデートに盛り込んでほしいひとは enchantMOONのIssue TrackerのFeature request: "MOONized" window.prompt にvoteしよう!(またか)

解説

「実行」ブロックは、任意のJavaScriptを指定できるブロックです。実はMOONBlockのすべてのブロックは「Done」を押してMOONBlockを終了するときにJavaScriptに変換されて保存されます(変換される内容は「Code」を押して見られます)。「実行」ブロックは、そうやって変換されるJavaScriptを直接入力できるのです。
 先述の内容をもう一度見てみましょう。

MOON.openUrl(window.prompt())


これは二つの命令から成り立っています。1つ目は MOON.openUrl(...)、2つ目は window.prompt()です。
 MOON.openUrl(...) は「ハイパーリンク」ブロックと同じ働きをします。「ハイパーリンク」ブロックはJavaScriptに変換されるとMOON.openUrl(...)というJavaScriptになります。つまり「ハイパーリンク」ブロックと同じことをしているわけです。
 window.prompt() はプロンプトを開く命令です。この二つを組み合わせて、プロンプトで入力されたURLをMOON.openUrl(...)に渡すということをしています。

 本来は「ハイパーリンク」ブロックとwindow.prompt()にあたる「文字入力」ブロックがあって、その組み合わせで作れればいいのですが、「文字入力」ブロックは現行のバージョンには存在しないし、「ハイパーリンク」ブロックは他のブロックからURLを受け取れるようにはなっていないので、「JavaScript」を使って実現しているわけなのです。


以上!

enchantMOONのストロークを90°回転させるシールつくった


enchantMOONストロークを回転させるシールをつくりました。シールをタップする毎に描画が時計周りに90°回転します。
※実際にデータを変更しますので、念のためバックアップを取ってからの実行をおすすめします。


ダウンロードはこちらから。

rotate90.moon.zip

注意

本シールを使用することによって発生した損害等については保証できませんので、自己責任でご使用ください。