銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

D3.js v4 でドラッグするには d3.drag() で behavior を取得する

f:id:takamints:20170418212231p:plain

D3.jsでドラッグイベントを処理する必要があったのですよ。

ほぼ初めてのD3ですからグーグル先生にいろいろ聞いて、「ほうほうなるほど」と学習していたのですけど、 ドラッグに関して各所で示されていたサンプル通りにやってみたら、まさかのエラー。

結局は、大きな問題ではありませんで、ひと言で言ってしまえばバージョン違いだったのですが、どうにも日本語の情報が少ないようなので書いておきます。

英語版です。v4に関して日本語の書籍が見当たらないです。

D3.js/v3でのコード

「D3.js ドラッグ」などと検索して出てくるサンプルコードの多くが、D3.jsのv3(バージョン3)のために書かれたコードで、最新のv4では動かないんですね。

v3でドラッグするには、d3.behavior.drag()で、Drag Behavior を取得するのですが、v4ではこれが動かない。

// D3.js v3でDrag Behaviorを取得

var drag = d3.behavior.drag(); //V4ではエラー

D3.js/v4でのOKコード

じゃあどうやるんだって―と、v4以降では以下のように、d3.drag()とするのだそうだ。

// D3.js/V4 でDrag Behaviorを取得

var drag = d3.drag(); // これでOK

リンク

リポジトリCHANGES にはしっかり書いてありました。

CHANGES
github.com

Dragに関する詳細は以下に
github.com

いやしかし

メジャーバージョンが上がっているので、APIに互換性がなくなってても文句は言わない約束だけど、 レガシーコードには警告を出すとかって対応があればモアベターね。

JavaScriptのラムダ式(アロー関数)は丸括弧で括らなければ即時実行できませんのね

f:id:takamints:20170328172435p:plain
Link: Flickr PAGE - CC BY 2.0

Node.jsで以下のようにラムダ式を即時実行していたのですが、ブラウザでは構文エラーとなって動かないんです。

(()=>{
    console.log("これ動きません");
}());

まさかコレが動かないとか思いもよらず。 どう見直しても問題があるとは思えなかったのだが動かないから仕方がない。

追記(2017-03-29)JavaScriptの言葉的には「ラムダ式」ではなく「アロー関数」のようですので、タイトルにのみ追記しました。

色々やってみた結果、ブラウザで動作させるには、以下のようにラムダ式全体を丸括弧で括る必要があるようですね。 しかし、なんだかカッコが多すぎ。ラムダ的に台無し感がありますなー。

((()=>{
    console.log("これでヨシ。だがしかし・・・");
})());

丸括弧なしでは関数オブジェクトとして評価されていないようです。 アロー演算子=>)の優先度の問題かな?

そもそも、どちらが正しいのかわからなかったので、調べてみると、どうやらECMAScript6の仕様のバグだそうで、 Node.jsでは便利な様に解釈して実装されているのかも知れませんが、今の所ブラウザではどうにもならないようですね。

d.hatena.ne.jp

即時実行するときはラムダ式を使わないというのが安全・安心? 関数内のthisが定義時にバインドされちゃうようなので、単なる無名関数の構文糖として使うのはマズいらしいし・・・

JavaScriptでマイクロ秒単位の定期処理を実行する(npm fractional-timer)

JavaScriptで1ミリ秒より短いインターバルタイマー処理を提供するモジュール fractional-timer のご紹介。

f:id:takamints:20170327172258p:plain
Link: Flickr PAGE - CC BY-SA 2.0

実際のところ、精度はよくありませんので、クリティカルな用途には向きません。 単純な処理をなるべく高速にタイマーで実行したいけど、標準タイマーの1ミリ秒では遅すぎる・・・といった時に使える感じ。 処理が重い場合は、呼び出し間隔は長くなります。

Chrome と Node.Js で、正しく動いていることを確認していますが、 FirefoxやEdgeでは、まともに動いていなかったので、ご注意下さい。

使い方

使い方は遅延時間に実数を指定できる点を除けば、標準のsetIntervalclearIntervalと同じです。 遅延時間は標準と同じくミリ秒単位で指定します(0.1=100マイクロ秒)。

以下のように呼び出すことで、タイマーの数と遅延時間等を調整して、定期処理を実行します。

(function() {
    "use strict";
    var ft = require("fractional-timer");
    var ftid = ft.setInterval(
        function(){
            ft.clearInterval(ftid);
        },
        0.001);// 1 microsecond
}());

インストー

npmでインストールすればNode.jsから使えます。

> npm install fraction-timer

ブラウザのWEBアプリでは、browserifyrequire するか、 scriptタグで読み込んで、FractionalTimerクラスの静的メソッドsetInterval/clearIntervalを直接使ってくださいね。

リポジトリ

www.npmjs.com

github.com

その他、能書き

やっていることは単純です。複数のインターバルタイマーで一つの処理を呼び出しているだけです。

例えば、1マイクロ秒で実行したい場合、1ミリ秒のタイマーを1000個使って呼び出すということです。

実際には、指定された間隔から算出される単位時間あたりの実行回数を計算し、タイマーの数と間隔を自動的に算出、ベストエフォートで呼び出します。

MZ-700フルJavaScriptエミュレータ」で、エミュレーションをワーカースレッドの定期処理で実装していて、当初は単一のインターバルタイマーで、最も高速になるように調整していたのですが、ある時タイマーが二重に動いてしまったことがあって、速度がなんと2倍程度に跳ね上がっていたのです。

「うわあ!世紀の大発見!!!」と思ったけれど、単一スレッド内ではシーケンシャル動作が保証されているので、当然の挙動なんですよね。

ただ、こういうことをやってる人があまりいないところを見ると、需要はないみたいなんですけどね(笑)