시각화/D3.js

this의 사용

비주얼라이즈 2015. 3. 13. 16:53


xScale과 yScale에서의 .domain()의 사용방법

xScale에서는 .domain( value )형태로 사용되었고, yScale에서는 .domain( [a,b])의 형태로 사용되었다. 이렇게 구분해서 사용한 이유는 표현하고자하는 방법에 차이가 있기 때문이다.





xScale에서의 사용법


이 예제에서는 x축을 '분절된 이산범위'로 활용하고자 d3.scale().ordinal()을 명시했다. 이에따라 .domain( ***)안에는 수열을 생성하는 d3.range(dataset.length)가 들어가게 된 것이다. 여기에 더해 함께 설명하자면, .rangeRoundBands([0,w], 0.05)는 d3.range(data.length)로 데이터셋크기만큼의 수열을 정의역으로 설정하였기에, 화면에 출력할 '치역'을 설정하는데 활용하였다. 


rangeRoundBands()는 rangeBand()와 유사하지만, 치역이 가장 가가운 정수 픽셀 값으로 반올림 된다. 예를들어 12.3456은 12가 된다. 시각적 요소를 가이드라인에 정확히 정렬시키는 데 사용하면 좋다.[각주:1]





yScale에서의 사용법


위에서 X축을 '분절된 이산범위'로 사용하고자 한 반면, y축은 데이터를 '연속범위'로 다루고자했다. 이에따라 x축의 경우와 다르게 d3.scale.linear()를 사용했고, .domain([시작 값, 종료 값])으로 지정한 것이다.












transition()의 지속시간 기본값은 1/4초(250밀리초)이며 duration()으로 조정할 수 있다. duration()은 반드시 transition()다음에 사용해야한다.


duration()으로 어느정도 지속시간을 설정해야하는가?

가장좋은것은 직접 여러번 하면서 최적의 시간대를 찾는 것이다. 친절하게도《d3.js》의 저자 스캇머레이(2014)는 이에 몇가지 경우에 따라 추천하는 시간을 적어두었다.


인터페이스를 위한 작은 반응(문서요소에 마우스를 올리는 경우처럼)을 위해서는 150밀리초 전후가 적당하고, 데이터 뷰를 다른 형태로 전환하는 경우처럼 중요한 시각화에 많은 트랜지션을적용할 때는 1,000밀리초가 적당하다. 1초(1,000밀리초)는 길지도 짧지도 않은 시간이다. 적절히 사용해야한다.[각주:2]







ease()


ease()란 transition에 사용되는 모션을 말한다. D3에서는 ease()에 관한 몇가지 모드를 지원한다. 기본값은 cube-in-out이며 이는 점진적으로 가속했다가 감속되는 효과이다. 가장무난하기때문에, 기억해두면 좋다.






delay()메서드


이름그대로 '지연'기능을 갖는다. 생각보다 이는 유용한데, 이 지연기능으로 "모션의 시작지점"을 조정 할 수 있기 때문이다. 예를들어 사용자가 클릭후 바로 실행되기보다 1초의 짧은시간 후에 동작하도록 하려면 다음의 순서로 적어주면 된다.




delay로 시간차를 준 트랜지션효과주기


다른 D3메서드를 다룰 대 이미 보았듯이, 익명 함수에는 해당 문서요소와 역인 데이터 d와 색인 값 i 가 전달인자로 넘어온다. 그래서 여기서도 D3는 매 원소를 순환하면서 각 문서요소의 지연값에 i * 100을 지정한다. 결국, 갈수록 100밀리초씩 더 지연된다.[각주:3]





그러나 이런방식은 데이터의 수가 많을경우, 문제가 생긴다.

전체 데이터길이에 영향을 받기 때문이다. 이러한 문제점을 해결하기위해 다음과 같은 방식으로 코드를 작성한다.





위 사진에 나오는 형태로 코드를 작성하면, 데이터 크기에 맞춰 delay를 적용할 수 있게된다. 여기서 1,000을 마지막에 곱해준 것은 delay의 단위가 밀리초이기 때문이다.(1,000밀리초 = 1초)



each()의 활용


each는 ease()처럼 트랜지션에 모션을 줄 수 있는 기능이지만, 시작과 끝을 지정해준다는 점에서 다르다. 



이전에 ease()을 .attr()이전에서 적용시켰던 것과 달리, each()는 transition()과 duration()보다 뒤이면서 attr()을 감싸는 형태로 작성한다.







this의 사용

each()의 인자로 넘어가는 익명 함수 안에 있는 this는 '해당 문서 요소'다. 함수안에서 해당 문서요소를 다시 선택해서 수정하는 작업을 할 때 이를 this로 참조할수 있어서 무척 유용하다.[각주:4]


하나의 문서요소 안에서, 트랜지션은 트랜지션을 덮어쓴다.

그러나 each()의 strat익명함수에서 transition을 적용하고자해도, 적용이 되지않는다. 하나의 문서요소에서 트랜지션은 한번만 동작한다. 따라서 each()안에서 트랜지션을 새로 적용하게되면, 새롭게 적용되는 트랜지션이 이전의 트랜지션을 덮어쓴다.


D3의 설계결함으로 보이지만, 이는 의도된 동작이다. 각기 다른 뷰를 실행하는 여러 버튼이 있고 사용자가 이 버튼들을 연속해서 재빨리 눌렀다고 가정하자. 먼저 선택한 트랜지션이 아닌 마지막으로 선택한 트랜지션이 동작하는 게 옳은 동작 방식이다.[각주:5]


제이쿼리(jQuery)에 익숙한 개발자라면, 여기서 차이점을 느끼게 된다. 기본적으로 제이쿼리는 트랜지션을 큐(queue)에 쌓는다. 그래서 트랜지션은 차례대로 실행되고, 실행 중에 신규 트랜지션이 임의로 끼어들지 않는다. 이런 동작 방시은 성가신 인터페이스를 만들기도한다. 예를 들면, 마우스를 올리면 페이드인(fade in)이 발생하는 메뉴의 경우, 마우스가 이미 벗어났어도 그 트랜지션이 끝나기 전에는 페이드아웃(fade out)이 일어나지 않는다.




each("start", ...)는 트랜지션이 없는 단순 트랜스폼(transform)작업을 위해서만 사용한다.

첫 번재 트랜지션이 시작되었으므로 코드는 해당 부분에서 잠시 '멈춘다'. 그리고 선택한 각 문서요소별로 each("start", ...)를 호출하자, each()안에서는 분홍색으로 바꾸는 두 번재 트랜지션이 생성되고, 첫 번째 트랜지션을 덮어버린다. 그래서 점들은 목표한 좌표로 가지 못한다.


 트랜지션의 이런 특징 때문에 each("start", ...)는 트랜지션이 없는 단순 트랜스폼(transform)작업을 위해서만 사용해야 한다는 사실을 기억하자.





하지만 each("end", ...)부분에서는 트랜지션 사용이 가능하다. each("end", ...)가 실행되는 시점은 주요 트랜지션이 이미 종료된 상태이기 때문이다. 그래서 새로운 트랜지션 생성이 문제를 일으키지 않는다.[각주:6]




  1. 스캇 머레이 지음, 변치훈 옮김, 『D3.js : 쉽고 빠른 인터랙티브 데이터 시각화』, 인사이트, 2014.3, 173쪽. [본문으로]
  2. 위의 책, 182쪽. [본문으로]
  3. 위의 책, 185쪽. [본문으로]
  4. 위의 책, 195쪽. [본문으로]
  5. 위의 책, 196쪽. [본문으로]
  6. 위의 책, 196쪽. [본문으로]