데이터 가시화 (d3.js , Plotly, Grafana, Kibana 등)

d3 Transitions 으로 작업하기 (번역)

[하마] 이승현 (wowlsh93@gmail.com) 2015. 7. 22. 08:46

원본내용중 누락부분이 있으니 , 전체를 꼼꼼히 보시려면 글 마지막의 레퍼런스를 참고하십시요.


Transitions 으로 작업하기 


D3’s selection.transition  메소드는 DOM 이 바뀔때 애니메이션(변환)을 쉽게 해준다.  예를들어 텍스트 색상을 빨강으로 바꾸기위해서 다음과 같이 body 엘리먼트를 선택하고 스타일 속성을 주는데..

d3.select("body").style("color", "red");

어느정도의 시간동안 변화를 주기위해서는 transition 을 사용한다.

d3.select("body").transition().style("color", "red");


선택할수있는 모든 요소들은 순간적인 변화 대신transition 을 통해 시간을 두고 애니메이션 할수있다. 

Transitions 은 작은 Animation 이다.


Transitions 는 시작과 끝의 2개의 키프레임만 가지고있는 key frame animation 이다. 시작 키 프레임은  일반적으로 DOM 의 현재 상태이고 , 마지막 키 프레임은 당신이 특정한 속성이나 스타일등의 셋이다.

다시 transition 예를 보면 : 

d3.select("body").transition().style("color", "red");

단지 하나의 속성( red ) 만 가지고있는데 , 이것의 스타팅 색깔은 DOM 의  getComputedStyle or getAttribute 를 통해서 가져온후 계산될것이다.


스타팅 색상을 더 명시적으로 선택하려면  transition 을 만들기전에 DOM 을 세팅하면 된다.

d3.select("body")
    .style("color", "green") // make the body green
  .transition()
    .style("color", "red"); // then transition to red

먼저 시작색상을 green 으로 세팅한후에 red 로 변화시켰다.


만약 transition  이 delay 를 가졌다면 시작 색상은 transition 가 시작될때 세팅되야하는데 이때 start event 로부터 listening  을 받아서 처리하면된다.


d3.select("body").transition()
    .delay(750)
    .each("start", function() { d3.select(this).style("color", "green"); })
    .style("color", "red");


더 눈에 뛰는  (explicit approach) 방법은 transition.styleTween 을 사용하는것인데, 이것은 스타팅 값 (색상) 을 DOM 으로 부터 계산하는것을 건너뛴다.


d3.select("body").transition()
    .styleTween("color", function() { return d3.interpolate("green", "red"); });

 

이 마지막 예제는 어떻게 transitions  이 작업하는지에 대한 힌트를 준다.

transition.style 를 사용할때 D3 는  DOM 으로부터 스타팅 값을 가져오고 특정 마지막 값을 보간기(interpolator) 를 생성해서 자동적으로  tween ( 2개 속성 사이의)   스타일을 만든다.

만약 당신이 스타팅 값이나 보간기(interpolator) 를  변경하길 원하면 styleTweenattrTween or tween. 를 사용해라.


Transitions 는 일정시간동안 값을 보간한다.

주어진 시작,마지막 키 프레임으로 어떻게 자연스로운 애니메이션 변환을 수행할수있을까? 

D3 가  어떻게 보간하고 블렌드(blend) 하는지 알아야한다. d3.interpolate 메소드는 적합한 보간기를 각 값의 쌍에 대한 타입을 추론해서 결정한다. D3 는 다음과 같은 공통 타입을 지원한다.

문자  interpolator 는 특별히 유용하다. 그것은 문자들 사이에 포함된 숫자를 발견한다. 

문자 interpolators 는 많은 응용을 가지는데  다음과 같다. interpolating path data (e.g., "M0,0L20,30") 와   CSS font specifications (e.g., "300 12px/100% Helvetica").


항상 문자 보간이 적절한것은 아니다.  시작과 끝의 패스 데이타가 같은 숫자의 컨트롤 포인트를 가지고있지 않다면  더이상 숫자들의 쌍이 의미있지 않다. 대신해서 보간하기 전에  resample the path  것이 필요하다.(또는 advanced shape blending algorithms 적용이 필요함).  마찬가지로 arcs  도  interpolation in polar coordinates 를 요구한다. 각도들이 보간될것이다.


만약 당신이 보간기를 직접 구현하길 원한다면 ,  0~1 사이의 범위를 갖는 함수를 구현해야한다. t = 0 이면 시작 값이고 1 이면 마지막 값이며 , 중간 값에 보간된값들을 리턴해주면된다. 다음은 두 값 사이의 값을 리턴해주는 단순 예제이다.

function interpolateNumber(a, b) {
  return function(t) {
    return a + t * (b - a);
  };
}


보간될수 없는것들 


엘리먼트를 생성하는 보간은 불가능핟. 단지 존재하거나 그렇지 않거나이다. transition를 스케쥴하기위해서 엘리먼트는 반드시 존재해야한다. Selection  메소드는 데이타 조인 (data,enter,exit) 와 연관되며 생성한 엘리먼트(append, insert) 는 오직 selections 상에서 수행가능하다. 

general update pattern 과 함께 transitions  를 사용할때 ,  먼저 enter 와 exit 를 사용한 selections  상에서 데이터 조인을 수행한다. 그리고 나서 각각의 subselection  을  transitions 한다.


var bar = svg.selectAll(".bar")
    .data(data, function(d) { return d.key; });

bar.enter().append("rect")
    .attr("class", "bar")
    … // initialize entering bars

bar.transition()
    … // transition entering + updating bars

bar.exit().transition()
    … // transition exiting bars
    .remove();

For convenience, there are a few exceptions to this rule. You can transition.remove to remove an element at the end of the transition; likewise, transition.text sets the text content at the start of the transition, without interpolating. In the future, transitions may support additional non-interpolatable operations, such as classed and html.

Transition 의 삶

As with concurrent programming, perhaps the trickiest aspect of transitions is that they happen over time rather than instantaneously. The code does not proceed in a single straightforward path, as when the page loads, but as a complex sequence of recurring callbacks. While you can safely ignore this complexity in many cases, you must understand the rules which govern the evaluation of transitions if you want to harness their full power.

Transitions have a four-phase life cycle:

  1. The transition is scheduled.
  2. The transition starts.
  3. The transition runs.
  4. The transition ends.

A transition is scheduled when it is created: when you call selection.transition, you are scheduling a transition. This is also when you call attrstyle, and other transition methods to define the ending key frame. Scheduling happens in your code (for example, in response to the user clicking a button), meaning that the code so far is fully synchronous. This makes it easier to debug, and easier to use ending values that depend on changing global state, such as a scale’s domain.

A transition starts based on its delay, which was specified when the transition was scheduled. If no delay was specified, then the transition starts as soon as possible, which is typically after a few milliseconds. The start event is then dispatched, and the transition initializes its tweens, which may involve retrieving starting values from the DOM and constructing interpolators. Deferring the initialization of tweens to start is necessary because starting values aren’t known until the transition starts. Therefore, if you use attrTweenstyleTween and other tween methods, keep in mind that your code will be evaluated asynchronously when the transition starts!

While the transition runs, its tweens are repeatedly invoked with values of t ranging from 0 to 1. In addition to delay and duration, transitions have easing to control timing. Easing distorts time, such as for slow-in and slow-out. Some easing functions may temporarily give values of t greater than 1 or less than 0; however, the ending time is always exactly 1 so that the ending value is set exactly when the transition ends. A transition ends based on the sum of its delay and duration. When a transition ends, the tweens are invoked a final time with t = 1, and then the end event is dispatched.

#Transitions Are per-Element and Exclusive

Each element transitions independently. When you create a transition from a selection, think of it as a set of transitions, one per element, rather than a single mega-transition running on multiple elements. Different elements can have different delays and duration, and even different easing and tweens. Additionally, transition events are dispatched separately for each element. When you receive an end event for a given element, its transition has ended, but other transitions may still be running on other elements.

For a given element, transitions are exclusive: only one transition can be running on the element at the same time. Starting a new transition on the element stops any transition that is already running. Interrupting a transition on the element has no effect on other elements, and multiple transitions can run concurrently on different elements. While only one transition can be running simultaneously on the element, multiple transitions can be scheduled. For example, you can schedule successive transitions using transition.transition, which creates a new transition whose delay immediately follows the existing transition.

For each element, sequences of transitions only advance forward in time. Transitions are assigned a monotonically-increasing identifier (id) when they are scheduled; each new transition idis greater than the last. When a transition starts on the element, it can only run if the transition isnewer than whatever previously ran on the same element. Thus, starting a new transition implicitly cancels any previous transitions—even if those old transitions have not yet started. This design eliminates the need to cancel transitions explicitly. An excellent illustration of this behavior is thestacked-to-grouped bar transition. This uses two chained transitions: the bars first slide right and narrow; then the bars drop to the baseline. The first transition also has a staggered delay. If you quickly toggle between stacked and grouped, notice the old transition is only interrupted when the new one starts, not when the new one is scheduled.

Similar to how data is bound to an element’s __data__ property, transitions are bound to a__transition__ property. When a transition is first scheduled on an element, this property is created; when the last scheduled transition ends, this property is likewise deleted. Inspecting this property in the console can be useful to debug which transitions are scheduled to run on which elements, as well as to inspect computed tweens and transition timing parameters. Because transitions are bound to elements, you can also reselect elements within a transition and modify tweens or timing. This is most common in conjunction with component-driven transitions, such as the axis component; use post-selection to customize the appearance of elements after invoking the component.

#Additional Reading

This tutorial covered most of the important details in the mechanics of transitions. I omitted an explanation of transition inheritance using transition.each; perhaps I’ll cover that in the future, though see #400 for an example. For more on how transitions should be used (and not just implemented), see my earlier post on object constancy and Heer & Robertson’s excellent paper,“Animated Transitions in Statistical Data Graphics”.





레퍼런스 :

http://bost.ocks.org/mike/transition/

http://blog.visual.ly/creating-animations-and-transitions-with-d3-js