web-dev-qa-db-fra.com

Invoquer un rappel à la fin d'une transition

Je dois créer une méthode FadeOut (similaire à jQuery) en utilisant D3.js . Ce que je dois faire est de régler l'opacité sur 0 en utilisant transition().

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

Le problème est que j'ai besoin d'un rappel pour savoir quand la transition est terminée. Comment puis-je implémenter un rappel?

94
Tony

Vous voulez écouter l'événement "end" de la transition.

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

// old way
d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
  • Cette démo utilise l'événement "end" pour enchaîner plusieurs transitions dans l'ordre.
  • Le exemple de donut fourni avec D3 l'utilise également pour chaîner plusieurs transitions.
  • Voici ma propre démo qui modifie le style des éléments au début et à la fin de la transition.

De la documentation pour transition.each([type],listener) :

Si le type est spécifié, ajoute un écouteur pour les événements de transition, prenant en charge les événements "début" et "fin". L'écouteur sera appelé pour chaque élément individuel de la transition, même si la transition a un retard et une durée constants. L'événement de démarrage peut être utilisé pour déclencher un changement instantané lorsque chaque élément commence à faire la transition. L'événement de fin peut être utilisé pour initier des transitions à plusieurs étapes en sélectionnant l'élément actuel, this, et en dérivant une nouvelle transition. Toutes les transitions créées lors de l'événement de fin hériteront de l'ID de transition actuel et ne remplaceront donc pas une transition plus récente précédemment planifiée.

Voir ce sujet du forum pour plus de détails.

Enfin, notez que si vous souhaitez simplement supprimer les éléments une fois leur transition terminée (une fois la transition terminée), vous pouvez utiliser transition.remove() .

130
Phrogz

Mike Bostock's solution pour v3 avec une petite mise à jour:

  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") });
65
kashesandr

Désormais, dans d3 v4.0, il existe une fonctionnalité permettant d’attacher explicitement des gestionnaires d’événements aux transitions:

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

Pour exécuter du code lorsqu'une transition est terminée, il vous suffit de:

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

Une approche légèrement différente qui fonctionne également lorsqu'il y a de nombreuses transitions avec plusieurs éléments s'exécutant simultanément:

var transitions = 0;

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

Ce qui suit est une autre version de solution de Mike Bostock, inspirée du commentaire de @hughes à la réponse de @ kashesandr. Il effectue un seul rappel à la fin de transition.

Étant donné une fonction 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);
}

... nous pouvons prolonger d3 comme suit:

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;
}

en tant que JSFiddle .

Utilisez transition.end(callback[, delayIfEmpty[, arguments...]]):

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

... ou avec un délai optionnel si transition est vide:

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

... ou avec les arguments optionnels callback:

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

d3.transition.end Appliquera le callback passé même avec un transition vide si le nombre de millisecondes est spécifié ou si le deuxième argument est la vérité. Cela transmettra également tous les arguments supplémentaires au callback (et uniquement à ces arguments). Ce qui est important, c’est pas par défaut d’appliquer le callback si transition est vide, ce qui est probablement une hypothèse plus sûre dans un tel cas.

6
milos

Mike Bostock solution amélioré de kashesandr + en passant des arguments à la fonction de rappel:

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");
0
int_ua