Bir geçişin sonunda bir geri arama çağırın


98

D3.js kullanarak bir FadeOut yöntemi (jQuery'ye benzer) yapmam gerekiyor . Yapmam gereken, kullanarak opaklığı 0 olarak ayarlamaktır transition().

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

Sorun şu ki, geçişin ne zaman bittiğini anlamak için bir geri aramaya ihtiyacım var. Geri aramayı nasıl uygulayabilirim?

Yanıtlar:


144

Geçişin "son" olayını dinlemek istiyorsunuz.

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

// old way
d3.select("#myid").transition().style("opacity","0").each("end", myCallback);
  • Bu demo , sırayla birçok geçişi zincirlemek için "son" olayını kullanır.
  • Halka, örneğin D3 ile birlikte, aynı zamanda birlikte zincire birden fazla geçiş kullanır.
  • İşte geçişin başında ve sonunda öğelerin stilini değiştiren kendi demom .

Belgelerden transition.each([type],listener):

Eğer tip belirtilirse, hem "start" ve "son" olayları destekleyen, geçiş olayları için bir dinleyici ekler. Dinleyici, geçişin sabit bir gecikmesi ve süresi olsa bile geçişteki her bir öğe için çağrılacaktır. Başlangıç ​​olayı, her öğe geçiş yapmaya başladığında anlık bir değişikliği tetiklemek için kullanılabilir. Bitiş olayı, mevcut öğeyi seçerek thisve yeni bir geçiş türeterek çok aşamalı geçişleri başlatmak için kullanılabilir . Bitiş etkinliği sırasında oluşturulan tüm geçişler, mevcut geçiş kimliğini devralacak ve bu nedenle, önceden planlanmış daha yeni bir geçişi geçersiz kılmayacaktır.

Daha fazla ayrıntı için konuyla ilgili bu forum başlığına bakın.

Son olarak, öğeleri soluklaştıktan sonra kaldırmak istiyorsanız (geçiş bittikten sonra), kullanabileceğinizi unutmayın transition.remove().


7
Çok teşekkür ederim. Bu BÜYÜK BÜYÜK bir kitaplıktır, ancak belgelerde önemli bilgileri bulmak o kadar kolay değildir.
Tony

9
Dolayısıyla, geçişin sonundan bu şekilde devam etmenin bu yolu ile ilgili sorunum, işlevinizi N kez çalıştırmasıdır (geçiş öğeleri kümesindeki N öğe için). Bu bazen ideal olmaktan uzaktır.
Steven Lu

2
Bende de aynı sorun var. Son kaldırıldıktan sonra işlevi bir kez çalıştırmasını isterdim
canyon289

1
Nasıl bir geri aramayı ancak bir için tüm geçişler tamamlandıktan d3.selectAll()sonra (her öğe bittikten sonra) nasıl gerçekleştirirsiniz? Diğer bir deyişle, tüm öğelerin geçişi tamamlandığında bir işlevi geri çağırmak istiyorum.
hobbes3

Merhaba, çubuk grafiğini yığın / gruplandırmak için ilk bağlantı, herhangi bir .eacholay dinleyicisi veya olay kullanmayan bir Gözlemlenebilir not defterine işaret ediyor "end". "Zincirleme" geçişler gibi görünmüyor. İkinci bağlantı, benim için yüklenmeyen bir github'a işaret ediyor.
The Red Pea

65

Mike Bostock'un küçük bir güncellemeyle v3 için çözümü :

  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
Seçim sıfır öğe içeriyorsa, geri arama asla tetiklenmez. Bunu düzeltmenin bir yoluif (transition.size() === 0) { callback(); }
hughes

1
if (! callback) callback = function () {}; neden anında geri dönmeyesiniz veya bir istisna yapmayasınız? Geçersiz bir geri arama, bu rutinin tüm amacını ortadan kaldırır, neden bunu kör bir saatçi gibi yapalım? :)
prizma

1
@kashesandr, kullanıcı aynı etkiyi yaşayacağından, hiçbir şey yapamaz: (geçişin sonunda geri arama çağrısı olmaz) function endall(transition, callback){ if(!callback) return; // ... } veya bu işlevi geri arama olmadan çağırmak en kesin olarak bir hata olduğundan, bir istisna dikişi atarak durumu ele almak için uygun yol olun Bu davanın çok karmaşık bir istisna gerektirmediğini düşünüyorum.İstisna function endall(transition, callback){ if(!callback) throw "Missing callback argument!"; // .. }
prizma

1
Dolayısıyla, ayrı enter()ve exit()geçişlerimiz olduğunda ve üçünün de bitmesini beklemek istediğimizde, üç kez çağrıldığından emin olmak için geri aramaya kod eklememiz gerekir, değil mi? D3 çok dağınık! Keşke başka bir kütüphane seçseydim.
Michael Scheper

1
Eklemeliyim, cevabınızın kavradığım bazı sorunları çözdüğünün farkındayım ve bunu uygulamak için bir yardımcı program işlevi yazabilirim. Ancak bunu uygulamanın zarif bir yolunu bulamadım ve yine de her geçiş için ek özelleştirmeye izin veriyorum, özellikle yeni ve eski veriler için geçişler farklı olduğunda. Eminim bir şey bulacağım ama ' tüm bu geçişler bittiğinde bu geri aramayı çağır', D3 kadar olgun bir kütüphanede kutudan çıkar çıkmaz desteklenmesi gereken bir kullanım durumu gibi görünüyor. Görünüşe göre yanlış kitaplığı seçtim - aslında D3'ün hatası değil. Her neyse, yardımın için teşekkürler.
Michael Scheper

44

Şimdi, d3 v4.0'da, olay işleyicilerini geçişlere açıkça eklemek için bir olanak vardır:

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

Bir geçiş tamamlandığında kodu yürütmek için ihtiyacınız olan tek şey:

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

Güzel. Olay işleyicileri iğrenç.
KFunk

Ayrıca, bir öğenin görünümden geçişinin yaygın bir kullanım durumunu işleyen transition.remove()( bağlantı ) da vardır : "" Seçilen her öğe için, öğenin başka etkin veya bekleyen geçişleri olmadığı sürece geçiş sona erdiğinde öğeyi kaldırır. öğesinin başka etkin veya bekleyen geçişleri var, hiçbir şey yapmıyor. "
brichins

9
Görünüşe göre buna geçişin uygulandığı PER öğesi deniyor, benim anlayışıma göre soru bu değil.
Taylor C. Beyaz

10

Her biri aynı anda çalışan birçok öğeye sahip birçok geçiş olduğunda da işe yarayan biraz farklı bir yaklaşım:

var transitions = 0;

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

Teşekkürler, bu benim için iyi çalıştı. Ayrı bir çubuk grafik yükledikten sonra x ekseni etiket yönünü otomatik olarak özelleştirmeye çalışıyordum. Özelleştirme yüklemeden önce etkili olamaz ve bu, bunu yapabileceğim bir olay kancası sağladı.
whitestryder

6

Aşağıdaki, Mike Bostock'un çözümünün başka bir versiyonudur ve @hughes'in @ kashesandr'ın cevabına yaptığı yorumdan esinlenmiştir. Sonunda tek bir geri arama yapar transition.

Bir dropişlev verildiğinde ...

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

... şu şekilde uzatabiliriz 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 olarak .

Kullanım transition.end(callback[, delayIfEmpty[, arguments...]]):

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

... veya transitionboşsa isteğe bağlı bir gecikmeyle :

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

... veya isteğe bağlı callbackbağımsız değişkenlerle:

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

d3.transition.endmilisaniye sayısı belirtilmişse veya ikinci bağımsız değişken doğruysa , geçen değeri callbackboş transition olsa bile uygular. Bu ayrıca herhangi bir ek argümanı (ve yalnızca bu argümanlara) iletecektir . Daha da önemlisi, bu varsayılan olarak if boştur ifadesini uygulamayacaktır , bu muhtemelen böyle bir durumda daha güvenli bir varsayımdır.callbackcallbacktransition


Bu güzel, beğendim.
kashesandr

1
Teşekkürler @kashesandr. Bu, başlangıçta cevabınızdan gerçekten ilham aldı!
milos

gerçekten bir bırakma işlevine veya argümanların aktarılmasına ihtiyacımız olduğunu düşünmeyin, çünkü aynı etki bir sarmalayıcı işlevi veya bağlama kullanılarak da elde edilebilir. Aksi takdirde harika bir çözüm olduğunu düşünüyorum +1
Ahmed Masud

Tıkır tıkır çalışıyor !
Benoît Sauvère

Bu yanıta bakın, .end () artık resmi olarak eklendi - stackoverflow.com/a/57796240/228369
chrismarx

5

D3 v5.8.0 + 'dan itibaren, bunu kullanarak yapmanın resmi bir yolu var. transition.end . Dokümanlar burada:

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

Bostock'tan çalışan bir örnek burada:

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

Ve temel fikir, .end()geçişin, tüm unsurların geçişi tamamlanana kadar çözülmeyecek bir söz vereceğidir:

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

Daha da fazlası için sürüm yayın notlarına bakın:

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


1
Bu, işleri halletmenin çok güzel bir yolu. Sadece benim gibi v5'in tamamını bilmeyen ve bunu uygulamak isteyenler için, <script src = " d3js.org/d3-transition.v1 kullanarak yeni geçiş kitaplığını içe aktarabilirsiniz. .min.js "> </ script >
DGill

0

Mike Bostock'un çözümü , kashesandr + geri arama işlevine argümanlar ileterek iyileştirildi :

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

Aslında bunu zamanlayıcı kullanarak yapmanın bir yolu daha var.

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

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

-2

Değişken kullanarak geçişler için bir süre belirleyerek benzer bir sorunu çözdüm. Sonra bir setTimeout()sonraki işlevi çağırırdım. Benim durumumda, örneğimde göreceğiniz gibi, geçiş ile sonraki arama arasında hafif bir örtüşme olmasını istedim:

var transitionDuration = 400;

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

setTimeout(function () {
  sortControl.forceSort();
}, (transitionDuration * 0.75)); 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.