何がわかりづらくて使いにくいのか段々わかってきた。
前回「animate で実現出来ない」と騒いでいた「一つ目の大きな原因」は、実は既にワタシは別の場所で気付いていたヤツだった。お試し初期で、cy.center() は普通のエンジニアが普通に期待する普通の振る舞いと異なる振る舞いをすると書いた。ちょっと説明不足だったが、cy.center に引数を与えないのは思ったとおりのことをする。eles を与える方が問題で、これは「eles を中心に持ってきてはくれず、fit に似た振る舞いをする」。これがそのまま cy.animate で書ける center にも適用される、つまり:
1 var j = cy.$('#j');
2 cy.animate({
3 center: {
4 eles: j,
5 }
6 }, {
7 duration: 1000
8 });
は、おそらく「10人中8人から9人、あるいは10人がビックリする」動きをする。そして「これが意図と違うから」ほかのもので代用しようとして困る、というのはまさしくワタシが panBy そのもので苦労した構造そのもの。
そして前回困ったのは「center + zoom の併せ技なので計算を考えるのがややこしい」ことで…、やっと書けたさ、「animation なし版」を:
1 var start_level = cy.zoom(); // 現在のズームレベル
2 var target_level = 4; // 目標(変更後)のズームレベル
3 var nmodpos = node.position(); // ノードのモデルポジション
4 // ノードのレンダーポジション (要するに html コンテナ内での描画位置(ピクセル座標))
5 var nrndpos = node.renderedPosition();
6 // 「原点」のレンダーポジションで、これは cy.zoom(target_level) しても移動しない
7 var orndpos = {
8 x: start_level * nmodpos.x - nrndpos.x,
9 y: start_level * nmodpos.y - nrndpos.y,
10 };
11 // ピクセル座標のどど真ん中
12 var target_pos = {
13 x: $(cy.container()).width() / 2,
14 y: $(cy.container()).height() / 2
15 };
16 // panBy で移動したいのは、「ズームによって視野範囲から
17 // ぶっ飛んでいった分の移動を「戻しつつ中心に」」。
18 var vec = {
19 x: (target_pos.x - (nmodpos.x * target_level - orndpos.x)),
20 y: (target_pos.y - (nmodpos.y * target_level - orndpos.y))
21 };
22 cy.zoom(target_level);
23 cy.panBy(vec);
なかなか正解に辿りつけなかったのは、「ビュー原点そのものはズームレベルを変えただけでは移動しない」ことに気付かなかったから。絵にしてもなかなか伝えづらいが、上の正解コードに基いて絵にしてみるといい。にしてもこういうの解決するたび思うけど、「わかってしまうとなんでこれがわからなかったんだ」というほど単純なことなんだよな。
これが書けてしまえばシンプルに cy.animate に乗っけるだけ:
1 cy.animate({
2 zoom: target_level,
3 panBy: vec
4 }, {
5 duration: 1500,
6 });
前回版で「これはヒドい」として挙げたコードの残り半分は相変わらずそのまま、つまり「flashClass はあんまし嬉しくない」は何も解決しないけれど、今回ここがキレイになっただけでかなり嬉しい。
それとね、「どうしてなんか使いにくく感じるのか」はこれは、やっぱ「地図系とは違うから」だね。地図系は基本的にほとんどが「緯度・軽度」という、cytoscape.js が言うところの「モデルポジション」だけをインターフェイスとしてるわけよ。レンダーポジションなんかほとんど意識することがなく済むわけね。だから cytoscape.js のインターフェイスをみてるとつい「モデルポジションを渡させちくり」と思っちゃうのね。まぁ…これはそういうもんだとして諦めるしかないね。
正直ほんとにこれしか変更してないようなもんだけど一応いつものように「実際に動くやつ」: