전환이 끝날 때 콜백 호출


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);
  • 이 데모 는 "end"이벤트를 사용하여 많은 전환을 순서대로 연결합니다.
  • D3와 함께 제공 되는 도넛 예제 에서도이를 사용하여 여러 전환을 연결합니다.
  • 다음 은 전환의 시작과 끝에서 요소의 스타일을 변경하는 자체 데모 입니다.

에 대한 문서에서 transition.each([type],listener):

유형 이 지정된 경우 "시작"및 "종료"이벤트를 모두 지원하는 전환 이벤트에 대한 리스너를 추가합니다. 리스너는 전환의 지연과 지속 시간이 일정하더라도 전환의 각 개별 요소에 대해 호출됩니다. 시작 이벤트는 각 요소가 전환을 시작할 때 즉각적인 변경을 트리거하는 데 사용할 수 있습니다. 종료 이벤트는 현재 요소를 선택하고 this새 전환을 파생 하여 다단계 전환을 시작하는 데 사용할 수 있습니다 . 종료 이벤트 중에 생성 된 전환은 현재 전환 ID를 상속하므로 이전에 예약 된 새로운 전환을 재정의하지 않습니다.

주제에 대한이 포럼 스레드를 참조하십시오. 대한 를 참조하십시오.

마지막으로 요소가 페이드 아웃 된 후 (전환이 완료된 후) 제거하려는 경우를 사용할 수 있습니다 transition.remove().


7
대단히 감사합니다. 이것은 GREAT GREAT 라이브러리이지만 문서에서 중요한 정보를 찾기가 쉽지 않습니다.
Tony

9
따라서 전환이 끝날 때부터 계속하는 방법에 대한 내 문제는 함수를 N 번 실행한다는 것입니다 (전환 요소 집합의 N 항목에 대해). 이것은 때때로 이상적과는 거리가 멀다.
Steven Lu

2
나는 같은 문제가 있습니다. 한번 마지막 제거한 후 기능을 실행 텐데
canyon289

1
a에 대한 모든 전환이 완료된 후에 만 콜백을 수행하는 방법은 d3.selectAll()무엇입니까 (대신 각 요소가 완료된 후)? 즉, 모든 요소가 전환을 마치면 하나의 함수 만 콜백하고 싶습니다.
hobbes3 2013-08-11

안녕하세요, 스택 / 그룹 막대 차트에 대한 첫 번째 링크는 .each이벤트 리스너 나 이벤트를 사용하지 않는 Observable 노트북을 가리 킵니다 "end". 전환을 "연결"하지 않는 것 같습니다. 두 번째 링크는 나를 위해로드되지 않는 github를 가리 킵니다.
The Red Pea

65

Mike Bostock의 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) callback = function () {}; 즉시 반환하거나 예외를 던지지 않는 이유는 무엇입니까? 유효하지 않은 콜백은이 루틴의 전체 목적을 무력화시킵니다. 왜 맹목적인 워치 메이커처럼 진행합니까? :)
prizma

1
(전환의 마지막에 콜백 호출) : 사용자가 같은 효과를 경험하게 될 것입니다 때문에 @kashesandr 하나는 단순히 아무것도 할 수 없다 function endall(transition, callback){ if(!callback) return; // ... } 는 콜백없이이 함수를 호출하면 오류가 가장 certanly 때문에, 또는에 예외 솔기를 던지고, 상황을 처리하는 적절한 방법이 function endall(transition, callback){ if(!callback) throw "Missing callback argument!"; // .. }
되십시오.

1
따라서 별도의 전환 enter()exit()전환이 있고 세 가지가 모두 완료 될 때까지 기다리려면 콜백에 코드를 넣어 세 번 호출되었는지 확인해야합니다. 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

뷰에서 요소를 전환하는 일반적인 사용 사례를 처리하는 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-09-17

6

다음은 Mike Bostock 솔루션 의 또 다른 버전이며 @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밀리 초 수가 지정 되거나 두 번째 인수가 진실 인 경우callback 비어있는 transition 경우 에도 전달 된 값을 적용합니다 . 또한 추가 인수를에 전달합니다 (및 해당 인수 만). 중요한 것은 이것은 기본적으로 if is empty를 적용 하지 않을 것이며 , 이는 아마도 그러한 경우에 더 안전한 가정 일 것입니다.callbackcallbacktransition


좋아요, 좋아요.
kashesandr

1
감사합니다 @kashesandr. 이것은 실제로 귀하의 답변에서 영감을 얻었습니다!
milos

래퍼 함수 나 bind를 사용하여 동일한 효과를 얻을 수 있으므로 드롭 함수 나 인수 전달이 필요하다고 생각하지 마십시오. 그렇지 않으면 나는 그것이 훌륭한 솔루션 +1 생각
아메드 마수드

매력처럼 작동합니다!
Benoît Sauvère

이 응답을 참조하십시오. .end ()가 공식적으로 추가되었습니다.- stackoverflow.com
a

5

D3 v5.8.0 +부터 .NET을 사용하는 공식적인 방법이 transition.end있습니다. 문서는 다음과 같습니다.

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

Bostock의 실제 사례는 다음과 같습니다.

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

기본 아이디어는를 추가하는 것만으로도 .end()전환이 모든 요소 전환이 완료 될 때까지 해결되지 않는 promise를 반환한다는 것입니다.

 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

Mike Bostock의 솔루션kashesandr + 콜백 함수에 인수를 전달 하여 개선되었습니다 .

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)); 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.