@@ -19,41 +19,38 @@ function hierarchicalChart(options) {
1919 . select ( container )
2020 . append ( 'svg' )
2121 . attr ( 'width' , options . width )
22- . attr ( 'height' , height + margin . top + margin . bottom ) ;
23-
24- x = d3 . scaleLinear ( ) . range ( [ 39 , height - margin . top ] ) ;
22+ . attr ( 'height' , height + margin . top + margin . bottom ) . call ( zoom ) ; ;
2523
2624 root = d3
2725 . hierarchy ( options . data )
2826 . sum ( ( d ) => d . annotated )
2927 . sort ( ( a , b ) => b . value - a . value )
3028 . eachAfter ( ( d ) => ( d . index = d . parent ? ( d . parent . index = d . parent . index + 1 || 0 ) : 0 ) ) ;
3129
32- x . domain ( [ 0 , root . value ] ) ;
30+ var y = d3
31+ . scaleLinear ( )
32+ . domain ( [ 0 , d3 . max ( root . children , ( d ) => d . value ) ] )
33+ . nice ( )
34+ . range ( [ height - margin . bottom , margin . top ] ) ;
3335
34- xAxis = ( g ) =>
35- g
36- . attr ( 'class' , 'x-axis' )
37- . attr ( 'transform' , `translate(40, 340) rotate(-90)` )
38- . call ( d3 . axisTop ( x ) . ticks ( height / 80 ) )
39- . call ( ( g ) => ( g . selection ? g . selection ( ) : g ) . append ( 'line' ) . select ( '.domain' ) . remove ( ) )
40- . selectAll ( 'text' )
41- . attr ( 'transform' , `rotate(90)` )
42- . attr ( 'x' , - 20 )
43- . attr ( 'y' , 0 )
44- . attr ( 'dy' , '.35em' ) ;
36+ var yAxis = ( g ) => g
37+ . attr ( 'transform' , `translate(${ margin . left } ,0)` )
38+ . call ( d3 . axisLeft ( y ) )
39+ . call ( ( g ) => g . select ( '.domain' ) ) ;
40+
41+ var x = d3
42+ . scaleBand ( )
43+ . domain ( root . children . map ( ( d ) => d . data . name ) )
44+ . range ( [ margin . left , width - margin . right ] )
45+ . padding ( 0.4 ) ;
4546
46- yAxis = ( g ) =>
47+ var xAxis = ( g ) =>
4748 g
48- . attr ( 'class' , 'y-axis' )
49- . attr ( 'transform' , `translate(0,300) rotate(-90)` )
50- . call ( ( g ) =>
51- g
52- . append ( 'line' )
53- . attr ( 'stroke' , 'currentColor' )
54- . attr ( 'y1' , margin . left )
55- . attr ( 'y2' , width - margin . right ) ,
56- ) ;
49+ . attr ( 'transform' , `translate(0,${ height - margin . bottom } )` )
50+ . call ( d3 . axisBottom ( x ) )
51+ . selectAll ( 'text' )
52+ . style ( 'text-anchor' , 'start' )
53+ . attr ( 'transform' , 'rotate(20 -10 10)' ) ;
5754
5855 svg
5956 . append ( 'rect' )
@@ -69,9 +66,9 @@ function hierarchicalChart(options) {
6966
7067 duration = 0 ;
7168
72- svg . append ( 'g' ) . call ( yAxis ) ;
69+ svg . append ( 'g' ) . attr ( 'class' , 'y-axis' ) . call ( yAxis ) ;
7370
74- svg . append ( 'g' ) . call ( xAxis ) ;
71+ svg . append ( 'g' ) . attr ( 'class' , 'x-axis' ) . call ( xAxis ) ;
7572
7673 down ( svg , root ) ;
7774
@@ -83,62 +80,41 @@ function hierarchicalChart(options) {
8380 // Rebind the current node to the background.
8481 svg . select ( '.background' ) . datum ( d ) ;
8582
86- // Define two sequenced transitions.
87- const transition1 = svg . transition ( ) . duration ( duration ) ;
88- const transition2 = transition1 . transition ( ) ;
89-
9083 // Mark any currently-displayed bars as exiting.
9184 const exit = svg . selectAll ( '.enter' ) . attr ( 'class' , 'exit' ) ;
9285
9386 // Entering nodes immediately obscure the clicked-on bar, so hide it.
9487 exit . selectAll ( 'rect' ) . attr ( 'fill-opacity' , ( p ) => ( p === d ? 0 : null ) ) ;
9588
9689 // Transition exiting bars to fade out.
97- exit . transition ( transition1 ) . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
98-
99- // Enter the new bars for the clicked-on data.
100- // Per above, entering bars are immediately visible.
101- const enter = bar ( svg , down , d , '.y-axis' ) . attr ( 'fill-opacity' , 0 ) ;
102-
103- // Have the text fade-in, even though the bars are visible.
104- enter . transition ( transition1 ) . attr ( 'fill-opacity' , 1 ) ;
105-
106- // Transition entering bars to their new y-position.
107- enter
108- . selectAll ( 'g' )
109- . attr ( 'transform' , stack ( d . index ) )
110- . transition ( transition1 )
111- . attr ( 'transform' , stagger ( ) ) ;
90+ exit . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
11291
11392 // Update the x-scale domain.
114- x . domain ( [ 0 , d3 . max ( d . children , ( d ) => d . value ) ] ) ;
93+ y . domain ( [ 0 , d3 . max ( d . children , ( d ) => d . value ) ] ) ;
11594
11695 // Update the x-axis.
117- svg . selectAll ( '.x -axis' ) . transition ( transition2 ) . call ( xAxis ) ;
96+ svg . selectAll ( '.y -axis' ) . call ( yAxis ) ;
11897
119- // Transition entering bars to the new x-scale.
120- enter
121- . selectAll ( 'g' )
122- . transition ( transition2 )
123- . attr ( 'transform' , ( d , i ) => `translate(0,${ barStep * i } )` ) ;
98+ // Enter the new bars for the clicked-on data.
99+ // Per above, entering bars are immediately visible.
100+ const enter = bar ( svg , down , d , '.x-axis' ) . attr ( 'fill-opacity' , 0 ) ;
101+
102+ // Have the text fade-in, even though the bars are visible.
103+ enter . attr ( 'fill-opacity' , 1 ) ;
124104
125105 // Color the bars as parents; they will fade to children if appropriate.
126106 enter
127107 . selectAll ( 'rect' )
128108 . attr ( 'fill' , color ( true ) )
129109 . attr ( 'fill-opacity' , 1 )
130- . transition ( transition2 )
131110 . attr ( 'fill' , ( d ) => color ( ! ! d . children ) )
132- . attr ( 'width ' , ( d ) => x ( d . value ) - x ( 0 ) ) ;
111+ . attr ( 'height ' , ( d ) => y ( 0 ) - y ( d . value ) ) ;
133112 }
134113
135114 function bar ( svg , down , d , selector ) {
136- barStep = ( width - margin . left - margin . right ) / d . children . length ;
137- barPadding = 0.4 ;
138115 const g = svg
139116 . insert ( 'g' , selector )
140117 . attr ( 'class' , 'enter' )
141- . attr ( 'transform' , `translate(60,340) rotate(-90)` )
142118 . attr ( 'text-anchor' , 'end' )
143119 . style ( 'font' , '10px sans-serif' ) ;
144120
@@ -149,20 +125,20 @@ function hierarchicalChart(options) {
149125 . attr ( 'cursor' , ( d ) => ( ! d . children ? null : 'pointer' ) )
150126 . on ( 'click' , ( event , d ) => ( d . value ? down ( svg , d ) : null ) ) ;
151127
152- bar
153- . append ( 'text' )
154- . attr ( 'x' , barStep / 8 )
155- . attr ( 'y' , - barPadding - 15 )
156- . attr ( 'dy' , '.35em' )
157- . text ( ( d ) => d . data . name )
158- . style ( 'text-anchor' , 'start' )
159- . attr ( 'transform' , 'rotate(120 10 10)' ) ;
128+ x = d3
129+ . scaleBand ( )
130+ . domain ( d . children . map ( ( d ) => d . data . name ) )
131+ . range ( [ margin . left , width - margin . right ] )
132+ . padding ( 0.4 ) ;
160133
161- bar
134+ svg . selectAll ( '.x-axis' ) . call ( xAxis ) ;
135+
136+ bar . attr ( 'class' , 'bars' )
162137 . append ( 'rect' )
163- . attr ( 'x' , x ( 0 ) )
164- . attr ( 'width' , ( d ) => x ( d . value ) - x ( 0 ) )
165- . attr ( 'height' , barStep * ( 1 - barPadding ) > 0 ? barStep * ( 1 - barPadding ) : 0 ) ;
138+ . attr ( 'x' , ( d ) => x ( d . data . name ) + ( x . bandwidth ( ) - d3 . min ( [ x . bandwidth ( ) , 150 ] ) ) / 2 )
139+ . attr ( 'y' , ( d ) => y ( d . value ) )
140+ . attr ( 'height' , ( d ) => y ( 0 ) - y ( d . value ) )
141+ . attr ( 'width' , d3 . min ( [ x . bandwidth ( ) , 150 ] ) ) ;
166142
167143 hierTip = options . tip ;
168144 hierTip
@@ -173,7 +149,7 @@ function hierarchicalChart(options) {
173149 . style ( 'padding' , '0px 5px 0px 5px' )
174150 . style ( 'border-radius' , '4px' )
175151 . style ( 'font-size' , '10px' )
176- . offset ( [ - 155 , barStep / 4 - barPadding ] )
152+ . offset ( [ - 10 , 0 ] )
177153 . html ( function ( event , d ) {
178154 return '<span>' + d . value + '</span>' ;
179155 } ) ;
@@ -202,74 +178,58 @@ function hierarchicalChart(options) {
202178 // Rebind the current node to the background.
203179 svg . select ( '.background' ) . datum ( d . parent ) ;
204180
205- // Define two sequenced transitions.
206- const transition1 = svg . transition ( ) . duration ( duration ) ;
207- const transition2 = transition1 . transition ( ) ;
208-
209181 // Mark any currently-displayed bars as exiting.
210182 const exit = svg . selectAll ( '.enter' ) . attr ( 'class' , 'exit' ) ;
211183
212184 // Update the x-scale domain.
213- x . domain ( [ 0 , d3 . max ( d . parent . children , ( d ) => d . value ) ] ) ;
185+ y . domain ( [ 0 , d3 . max ( d . parent . children , ( d ) => d . value ) ] ) ;
214186
215187 // Update the x-axis.
216- svg . selectAll ( '.x -axis' ) . transition ( transition1 ) . call ( xAxis ) ;
188+ svg . selectAll ( '.y -axis' ) . call ( yAxis ) ;
217189
218- // Transition exiting bars to the new x-scale.
219- exit . selectAll ( 'g' ) . transition ( transition1 ) . attr ( 'transform' , stagger ( ) ) ;
220-
221- // Transition exiting bars to the parent’s position.
222- exit . selectAll ( 'g' ) . transition ( transition2 ) . attr ( 'transform' , stack ( d . index ) ) ;
223190
224191 // Transition exiting rects to the new scale and fade to parent color.
225192 exit
226193 . selectAll ( 'rect' )
227- . transition ( transition1 )
228- . attr ( 'width' , ( d ) => x ( d . value ) - x ( 0 ) )
194+ . attr ( 'width' , ( d ) => d3 . min ( [ x . bandwidth ( ) , 150 ] ) )
229195 . attr ( 'fill' , color ( true ) ) ;
230196
231197 // Transition exiting text to fade out.
232198 // Remove exiting nodes.
233- exit . transition ( transition2 ) . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
199+ exit . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
234200
235201 // Enter the new bars for the clicked-on data's parent.
236202 const enter = bar ( svg , down , d . parent , '.exit' ) . attr ( 'fill-opacity' , 0 ) ;
237203
238- enter . selectAll ( 'g' ) . attr ( 'transform' , ( d , i ) => `translate(0,${ barStep * i } )` ) ;
239-
240- // Transition entering bars to fade in over the full duration.
241- enter . transition ( transition2 ) . attr ( 'fill-opacity' , 1 ) ;
204+ enter . attr ( 'fill-opacity' , 1 ) ;
242205
243- // Color the bars as appropriate.
244- // Exiting nodes will obscure the parent bar, so hide it.
245- // Transition entering rects to the new x-scale.
246- // When the entering parent rect is done, make it visible!
247206 enter
248207 . selectAll ( 'rect' )
249208 . attr ( 'fill' , ( d ) => color ( ! ! d . children ) )
250- . attr ( 'fill-opacity' , ( p ) => ( p === d ? 0 : null ) )
251- . transition ( transition2 )
252- . attr ( 'width' , ( d ) => x ( d . value ) - x ( 0 ) )
209+ . attr ( 'height' , ( d ) => y ( 0 ) - y ( d . value ) )
253210 . on ( 'end' , function ( p ) {
254211 d3 . select ( this ) . attr ( 'fill-opacity' , 1 ) ;
255212 } ) ;
256213 }
257214
258- function stack ( i ) {
259- let value = 0 ;
260- return ( d ) => {
261- const t = `translate(${ x ( value ) - x ( 0 ) } ,${ barStep * i } )` ;
262- value += d . value ;
263- return t ;
264- } ;
215+ function zoom ( svg ) {
216+ const extent = [
217+ [ margin . left , margin . top ] ,
218+ [ width - margin . right , height - margin . top ] ,
219+ ] ;
220+
221+ svg . call (
222+ d3 . zoom ( ) . scaleExtent ( [ 1 , 5 ] ) . translateExtent ( extent ) . extent ( extent ) . on ( 'zoom' , zoomed ) ,
223+ ) ;
224+
225+ function zoomed ( event , d ) {
226+ x . range ( [ margin . left , width - margin . right ] . map ( ( d ) => event . transform . applyX ( d ) ) ) ;
227+ svg
228+ . selectAll ( '.bars rect' )
229+ . attr ( 'x' , ( d ) => x ( d . data . name ) )
230+ . attr ( 'width' , x . bandwidth ( ) ) ;
231+ svg . selectAll ( '.x-axis' ) . call ( xAxis ) ;
232+ }
265233 }
266234
267- function stagger ( ) {
268- let value = 0 ;
269- return ( d , i ) => {
270- const t = `translate(${ x ( value ) - x ( 0 ) } ,${ barStep * i } )` ;
271- value += d . value ;
272- return t ;
273- } ;
274- }
275235}
0 commit comments