एक संक्रमण के अंत में एक कॉलबैक आह्वान करें


98

मुझे D3.js का उपयोग करके एक फीडेओट विधि (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);
  • यह डेमो क्रम में कई बदलावों की श्रृंखला के लिए "अंत" घटना का उपयोग करता है।
  • डोनट उदाहरण है कि डी 3 के साथ जहाज भी इस श्रृंखला के लिए एक साथ कई बदलाव उपयोग करता है।
  • यहां मेरा अपना डेमो है जो संक्रमण की शुरुआत और अंत में तत्वों की शैली को बदलता है।

के लिए प्रलेखन से transition.each([type],listener):

यदि प्रकार निर्दिष्ट किया जाता है, तो संक्रमण घटनाओं के लिए एक श्रोता जोड़ता है, दोनों "प्रारंभ" और "अंत" घटनाओं का समर्थन करता है। संक्रमण में प्रत्येक व्यक्तिगत तत्व के लिए श्रोता को आमंत्रित किया जाएगा, भले ही संक्रमण में लगातार देरी और अवधि हो। आरंभिक घटना का उपयोग तात्कालिक परिवर्तन को ट्रिगर करने के लिए किया जा सकता है क्योंकि प्रत्येक तत्व संक्रमण शुरू होता है। अंतिम घटना का उपयोग वर्तमान तत्व का चयन करके this, और एक नया संक्रमण प्राप्त करके बहु-चरण संक्रमण शुरू करने के लिए किया जा सकता है । अंतिम ईवेंट के दौरान बनाया गया कोई भी संक्रमण वर्तमान ट्रांज़िशन आईडी को इनहेरिट करेगा, और इस तरह पहले से निर्धारित एक नए ट्रांज़िशन को ओवरराइड नहीं करेगा।

देखें विषय पर इस मंच धागा अधिक जानकारी के लिए।

अंत में, ध्यान दें कि यदि आप तत्वों को निकालने के बाद उन्हें हटाना चाहते हैं (संक्रमण समाप्त होने के बाद), तो आप उपयोग कर सकते हैं transition.remove()


7
आपका बहुत बहुत धन्यवाद। यह एक महान महान पुस्तकालय है, लेकिन प्रलेखन में महत्वपूर्ण जानकारी प्राप्त करना इतना आसान नहीं है।
टोनी

9
इसलिए, संक्रमण के अंत से जारी रखने के इस तरीके के साथ मेरी समस्या यह है कि यह आपके फ़ंक्शन N को चलाता है (संक्रमण तत्वों के सेट में एन आइटम के लिए)। यह कभी-कभी आदर्श से बहुत दूर है।
स्टीवन लू

2
मेरे साथ भी वही दिक्कत है। काश यह अंतिम
निष्कासन के

1
एक (सभी तत्व समाप्त होने के बाद) के लिए सभी संक्रमण समाप्त होने के बाद ही आप कॉलबैक कैसे करते हैं d3.selectAll()? दूसरे शब्दों में, मैं केवल एक फ़ंक्शन कॉलबैक करना चाहता हूं क्योंकि सभी तत्व संक्रमण को समाप्त करते हैं।
hobbes3

नमस्ते, स्टैक / ग्रुप बार चार्ट का पहला लिंक एक अवलोकन योग्य नोटबुक को इंगित करता है जो किसी भी .eachईवेंट श्रोता का उपयोग नहीं करता है, न ही "end"ईवेंट। यह "श्रृंखला" संक्रमण नहीं लगता है। दूसरा लिंक एक गितुब की ओर इशारा करता है जो मेरे लिए लोड नहीं करता है।
लाल मटर

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
यदि चयन में शून्य तत्व हैं, तो कॉलबैक कभी भी फायर नहीं करेगा। इसे ठीक करने का एक तरीका हैif (transition.size() === 0) { callback(); }
ह्यूजेस

1
if ((कॉलबैक) callback = function () {}; तुरंत क्यों नहीं लौटते हैं, या एक अपवाद नहीं फेंकते हैं? एक अमान्य कॉलबैक इस रूटीन के पूरे उद्देश्य को पराजित करता है, एक अंधे प्रहरी की तरह इसके साथ क्यों गुजरें? :)
prizma

1
@kashesandr कोई भी कुछ नहीं कर सकता है, क्योंकि उपयोगकर्ता एक ही प्रभाव का अनुभव करेगा: (संक्रमण के अंत में कोई कॉलबैक कॉल नहीं) function endall(transition, callback){ if(!callback) return; // ... } या, चूंकि यह सबसे प्रामाणिक रूप से कॉलबैक के बिना इस फ़ंक्शन को कॉल करने के लिए एक त्रुटि है, एक अपवाद सीम को फेंकना। स्थिति को संभालने का उपयुक्त तरीका हो मुझे लगता है कि इस मामले को बहुत जटिल होने की आवश्यकता नहीं है अपवाद function endall(transition, callback){ if(!callback) throw "Missing callback argument!"; // .. }
prizma

1
इसलिए जब हमारे पास अलग-अलग enter()और exit()बदलाव होते हैं, और जब तक तीनों खत्म नहीं हो जाते, तब तक इंतजार करना चाहते हैं, हमें यह सुनिश्चित करने के लिए कॉलबैक में कोड डालना होगा कि यह तीन बार सही है? डी 3 कितना गन्दा है! काश मैं एक और पुस्तकालय चुनता।
माइकल शेपर

1
मुझे जोड़ना चाहिए, मुझे पता है कि आपके उत्तर में कुछ समस्याएं हैं जिनके बारे में मैंने पकड़ लिया है, और मैं इसे लागू करने के लिए एक उपयोगिता फ़ंक्शन लिख सकता हूं। लेकिन मुझे इसे लागू करने का एक सुरुचिपूर्ण तरीका नहीं मिला है और फिर भी प्रत्येक संक्रमण के लिए अतिरिक्त अनुकूलन की अनुमति देता है, खासकर जब नए और पुराने डेटा के लिए संक्रमण भिन्न होते हैं। मुझे यकीन है कि मैं कुछ के साथ आऊंगा, लेकिन 'इस कॉलबैक को तब लागू करें जब ये सभी संक्रमण समाप्त हो गए हों' ऐसा लगता है कि एक उपयोग केस की तरह है, जिसे बॉक्स से बाहर डी 3 के रूप में परिपक्व होने का समर्थन किया जाना चाहिए। तो ऐसा लगता है कि मैंने गलत लाइब्रेरी को चुना है - वास्तव में डी 3 की गलती नहीं है। Anyhoo, आपकी मदद के लिए धन्यवाद।
माइकल शेपर

44

अब, d3 v4.0 में, स्पष्ट रूप से ईवेंट हैंडलर्स को संक्रमणों में संलग्न करने के लिए एक सुविधा है:

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

जब एक संक्रमण पूरा हो गया है तो कोड निष्पादित करने के लिए, आपको बस आवश्यकता है:

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

सुंदर। इवेंट हैंडलर्स ग्रॉस हैं।
1

एक लिंकtransition.remove() ( लिंक ) भी है , जो किसी तत्व को दृश्य से परिवर्तित करने के एक सामान्य उपयोग के मामले को संभालता है: `प्रत्येक चयनित तत्व के लिए, संक्रमण समाप्त होने पर तत्व को हटा देता है, जब तक कि तत्व में कोई अन्य सक्रिय या लंबित संक्रमण न हो। तत्व में अन्य सक्रिय या लंबित बदलाव हैं, कुछ भी नहीं करता है। "
ब्रिचिन

9
ऐसा लगता है कि इसे प्रति तत्व कहा जाता है जिसे संक्रमण पर लागू किया जाता है, जो मेरी समझ से संबंधित नहीं है।
टेलर सी। व्हाइट

10

थोड़ा अलग दृष्टिकोण जो एक साथ चलने वाले कई तत्वों के साथ कई बदलाव होने पर भी काम करता है:

var transitions = 0;

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

धन्यवाद, यह अच्छी तरह से मेरे लिए काम किया। मैं असतत बार चार्ट लोड करने के बाद स्वचालित रूप से x- अक्ष लेबल अभिविन्यास को अनुकूलित करने की कोशिश कर रहा था। अनुकूलन लोड होने से पहले प्रभावी नहीं हो सकता है, और इसने एक घटना हुक प्रदान किया जिसके माध्यम से मैं यह कर सकता था।
श्वेतपत्री

6

निम्नलिखित माइक बोस्कॉक के समाधान का एक और संस्करण है और @ केशेन्डर के उत्तर पर @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यदि मिलिसेकंड की संख्या निर्दिष्ट है या यदि दूसरा तर्क सत्य है, तोcallback रिक्त के साथ भी पास को लागू किया जाएगा । यह (और केवल उन तर्कों) के लिए कोई अतिरिक्त तर्क भी अग्रेषित करेगा । महत्वपूर्ण रूप से, यह डिफ़ॉल्ट रूप से लागू नहीं होगा यदि खाली है, जो संभवतः ऐसे मामले में एक सुरक्षित धारणा है।transition callbackcallbacktransition


यह अच्छा है, मुझे यह पसंद है।
काश्नरैंड

1
साभार @kashesandr यह वास्तव में आपके उत्तर से शुरू होने के लिए प्रेरित था!
मील

वास्तव में नहीं लगता कि हमें एक ड्रॉप फ़ंक्शन या तर्कों को पारित करने की आवश्यकता है, क्योंकि एक ही प्रभाव एक आवरण फ़ंक्शन द्वारा या बाइंड का उपयोग करके प्राप्त किया जा सकता है। अन्यथा मुझे लगता है कि यह एक महान समाधान है +1
अहमद मसूद

एक जादू की तरह काम करता है !
बेनोइट सॉवेरे

यह प्रतिक्रिया देखें, .end () अब आधिकारिक रूप से जोड़ दिया गया है - stackoverflow.com/a/57796240/228369
chrismarx

5

D3 v5.8.0 + के रूप में, अब इसका उपयोग करने का एक आधिकारिक तरीका है transition.end। डॉक्स यहां हैं:

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

बेसिकॉक से एक काम का उदाहरण यहाँ है:

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

माइक बोस्कॉक के समाधान में सुधार के लिए कॉलबैक फ़ंक्शन द्वारा पासिंग तर्क दिए गए:

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

वास्तव में टाइमर का उपयोग करके ऐसा करने का एक और तरीका है।

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)); 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.