トランジションの最後にコールバックを呼び出す


98

D3.jsを使用してFadeOutメソッド(jQueryに類似)を作成する必要があります。を使用して、不透明度を0に設定する必要がありtransition()ます。

d3.select("#myid").transition().style("opacity", "0");

問題は、トランジションがいつ終了したかを認識するためのコールバックが必要なことです。コールバックを実装するにはどうすればよいですか?

回答:


144

トランジションの「終了」イベントをリッスンしたい。

// d3 v5
d3.select("#myid").transition().style("opacity","0").on("end", myCallback);

// old way
d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
  • このデモでは、「終了」イベントを使用して、多くの遷移を順番にチェーンします。
  • ドーナツ例 D3と船も一緒にチェーンに複数のトランジションをこれを使用しています。
  • ここにあります私自身のデモの移行の開始時と終了時に要素のスタイルを変更します。

のドキュメントからtransition.each([type],listener)

タイプが指定されている場合、遷移イベントのリスナーを追加し、「開始」イベントと「終了」イベントの両方をサポートします。トランジションに一定の遅延と期間がある場合でも、リスナーはトランジション内の個々の要素ごとに呼び出されます。開始イベントを使用して、各要素が遷移を開始するときに瞬間的な変更をトリガーできます。終了イベントを使用して、現在の要素を選択しthis、新しい遷移を導出することにより、多段階遷移を開始できます。終了イベント中に作成された遷移は、現在の遷移IDを継承するため、以前にスケジュールされた新しい遷移を上書きしません。

詳細については、このトピックに関するこのフォーラムのスレッドを参照してください。

最後に、要素がフェードアウトした後(遷移が終了した後)に要素を削除したいだけの場合は、を使用できることに注意してくださいtransition.remove()


7
どうもありがとうございました。これはすばらしいライブラリですが、ドキュメントで重要な情報を見つけるのは簡単ではありません。
トニー・

9
したがって、遷移の最後から続行するこの方法の私の問題は、関数がN回(遷移する要素のセット内のN個の項目に対して)実行されることです。これは理想とはほど遠いことがあります。
Steven Lu

2
同じ問題があります。最後の削除後に関数が1回実行されるようにしたい
canyon289

1
すべての遷移がd3.selectAll()(各要素が終了した後ではなく)終了した後にのみ、どのようにコールバックを実行しますか?つまり、すべての要素の遷移が完了したら、1つの関数をコールバックするだけです。
hobbes3

こんにちは、スタック/グループ棒グラフへの最初のリンクは、.eachイベントリスナーもイベントも使用しないObservableノートブックを指しています"end"。遷移を「連鎖」させていないようです。2番目のリンクは、ロードされないgithubを指しています。
Red Pea

65

マイクボストックのv3向けのソリューションと小さな更新:

  function endall(transition, callback) { 
    if (typeof callback !== "function") throw new Error("Wrong callback in endall");
    if (transition.size() === 0) { callback() }
    var n = 0; 
    transition 
        .each(function() { ++n; }) 
        .each("end", function() { if (!--n) callback.apply(this, arguments); }); 
  } 

  d3.selectAll("g").transition().call(endall, function() { console.log("all done") });

5
選択にゼロ要素が含まれている場合、コールバックは発生しません。これを修正する1つの方法は次のif (transition.size() === 0) { callback(); }
とおり

1
if(!callback)callback = function(){}; なぜすぐに戻らないか、例外をスローしませんか?無効なコールバックは、このルーティンの目的全体を無効にします。なぜ盲目の時計職人のようにそれをやり遂げるのですか :)
prizma 2016年

1
@kashesandrユーザーは同じ効果を経験するため、何もすることができません:(遷移の最後にコールバック呼び出しがない)、function endall(transition, callback){ if(!callback) return; // ... } またはコールバックなしでこの関数を呼び出すのはほとんどの場合エラーなので、例外シームをスローしますこのケースはあまり複雑な例外を必要としないと私は思う状況を処理するための適切な方法である function endall(transition, callback){ if(!callback) throw "Missing callback argument!"; // .. }
prizma

1
我々は独立していたときにそうenter()exit()遷移を、3つすべてが完了するまで待ちたい、我々は確かにそれは右、3回呼び出されています作るために、コールバックにコードを配置する必要がありますか?D3はとても厄介です!別のライブラリを選択したいのですが。
Michael Scheper

1
私は付け加えておきます。あなたの答えが私が握っていたいくつかの問題を解決し、それを適用するユーティリティ関数を書くことができることを理解しています。しかし、私はそれを適用するエレガントな方法を見つけていませんが、特に新しいデータと古いデータのトランジションが異なる場合は、トランジションごとに追加のカスタマイズを許可します。私は何かを思い付くと確信していますが、「これらすべての遷移が終了したらこのコールバックを呼び出す」は、D3と同じくらい成熟したライブラリで、箱から出してサポートする必要があるユースケースのようです。したがって、私は間違ったライブラリを選択したようです。実際にはD3のせいではありません。Anyhoo、あなたの助けに感謝します。
Michael Scheper

44

現在、d3 v4.0では、遷移にイベントハンドラーを明示的にアタッチする機能があります。

https://github.com/d3/d3-transition#transition_on

遷移が完了したときにコードを実行するために必要なのは、次のとおりです。

d3.select("#myid").transition().style("opacity", "0").on("end", myCallback);

綺麗な。イベントハンドラーは粗雑です。
KFunk 2016

transition.remove()link)もあります。これは、ビューから要素を遷移する一般的なユースケースを処理します。要素には他のアクティブまたは保留中の遷移があり、何もしません。」
brichins

9
これは、遷移が適用されるPER要素と呼ばれているように見えますが、これは私の理解からは問題ではありません。
テイラーCホワイト

10

同時に実行される多くの要素を持つ多くの遷移がある場合にも機能する、少し異なるアプローチ:

var transitions = 0;

d3.select("#myid").transition().style("opacity","0").each( "start", function() {
        transitions++;
    }).each( "end", function() {
        if( --transitions === 0 ) {
            callbackWhenAllIsDone();
        }
    });

おかげで、うまくいきました。個別の棒グラフを読み込んだ後、x軸のラベルの向きを自動的にカスタマイズしようとしていました。ロードの前にカスタマイズを有効にすることはできません。これにより、これを実行するためのイベントフックが提供されました。
whitestryder 2015

6

以下は、マイクボストックのソリューションの別のバージョンであり、@ kashesandrの回答に対する@hughesのコメントに触発されました。transition終了時に単一のコールバックを行います。

与えられたdrop関数...

function drop(n, args, callback) {
    for (var i = 0; i < args.length - n; ++i) args[i] = args[i + n];
    args.length = args.length - n;
    callback.apply(this, args);
}

...次のd3ように拡張できます。

d3.transition.prototype.end = function(callback, delayIfEmpty) {
    var f = callback, 
        delay = delayIfEmpty,
        transition = this;

    drop(2, arguments, function() {
        var args = arguments;
        if (!transition.size() && (delay || delay === 0)) { // if empty
            d3.timer(function() {
                f.apply(transition, args);
                return true;
            }, typeof(delay) === "number" ? delay : 0);
        } else {                                            // else Mike Bostock's routine
            var n = 0; 
            transition.each(function() { ++n; }) 
                .each("end", function() { 
                    if (!--n) f.apply(transition, args); 
                });
        }
    });

    return transition;
}

JSFiddleとして

使用transition.end(callback[, delayIfEmpty[, arguments...]])

transition.end(function() {
    console.log("all done");
});

...またはtransitionが空の場合はオプションの遅延:

transition.end(function() {
    console.log("all done");
}, 1000);

...またはオプションのcallback引数:

transition.end(function(x) {
    console.log("all done " + x);
}, 1000, "with callback arguments");

d3.transition.endミリ秒数が指定されている場合または 2番目の引数が真である場合callback空の渡されたものも適用します。これにより、追加の引数も(およびそれらの引数のみ)に転送されます。重要なのは、これはデフォルトでif が空に適用されないことです。これは、そのような場合にはおそらくより安全な仮定です。transition callbackcallbacktransition


それはいいです、私はそれが好きです。
kashesandr 2015年

1
@kashesandrに感謝します。これは確かに、最初にあなたの答えに触発されました!
ミロス2015年

同じ効果はラッパー関数によって、またはバインドを利用することによって達成できるため、実際にはドロップ関数や引数の受け渡しが必要だとは思わないでください。そうでなければ私はそれが素晴らしい解決策だと思います+1
Ahmed Masud

魅力的な作品!
ブノワSauvère

)(この応答、.ENDを参照してください。これで正式に追加されました- stackoverflow.com/a/57796240/228369
chrismarx

5

D3のv5.8.0の+のように、今使ってこれを行うための公式の方法がありますtransition.end。ドキュメントはここにあります:

https://github.com/d3/d3-transition#transition_end

Bostockの実際の例は次のとおりです。

https://observablehq.com/@d3/transition-end

そして基本的な考え方は、を追加するだけで、.end()すべての要素の移行が完了するまで解決されないプロミスを返すというものです。

 await d3.selectAll("circle").transition()
      .duration(1000)
      .ease(d3.easeBounce)
      .attr("fill", "yellow")
      .attr("cx", r)
    .end();

詳細については、バージョンのリリースノートを参照してください。

https://github.com/d3/d3/releases/tag/v5.8.0


1
これは物事を処理する非常に良い方法です。私が言うには、v5のすべてを知らない私のようにこれだけを実装したい場合は、<script src = " d3js.org/d3-transition.v1を使用して新しい移行ライブラリをインポートできます。.min.js "> </ script >
DGill

0

kashesandr +コールバック関数に引数を渡すことで改善されたMike Bostockのソリューション

function d3_transition_endall(transition, callback, arguments) {
    if (!callback) callback = function(){};
    if (transition.size() === 0) {
        callback(arguments);
    }

    var n = 0;
    transition
        .each(function() {
            ++n;
        })
        .each("end", function() {
            if (!--n) callback.apply(this, arguments);
    });
}

function callback_function(arguments) {
        console.log("all done");
        console.log(arguments);
}

d3.selectAll("g").transition()
    .call(d3_transition_endall, callback_function, "some arguments");

-2

実際には、タイマーを使用してこれを行う方法がもう1つあります。

var timer = null,
    timerFunc = function () {
      doSomethingAfterTransitionEnds();
    };

transition
  .each("end", function() {
    clearTimeout(timer);
    timer = setTimeout(timerFunc, 100);
  });

-2

変数を使用して遷移に期間を設定することで、同様の問題を解決しました。次にsetTimeout()、次の関数を呼び出すために使用しました。私の例では、私の例でわかるように、遷移と次の呼び出しの間にわずかなオーバーラップが必要でした。

var transitionDuration = 400;

selectedItems.transition().duration(transitionDuration).style("opacity", .5);

setTimeout(function () {
  sortControl.forceSort();
}, (transitionDuration * 0.75)); 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.