@@ -19,41 +19,38 @@ function hierarchicalChart(options) {
19
19
. select ( container )
20
20
. append ( 'svg' )
21
21
. 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 ) ; ;
25
23
26
24
root = d3
27
25
. hierarchy ( options . data )
28
26
. sum ( ( d ) => d . annotated )
29
27
. sort ( ( a , b ) => b . value - a . value )
30
28
. eachAfter ( ( d ) => ( d . index = d . parent ? ( d . parent . index = d . parent . index + 1 || 0 ) : 0 ) ) ;
31
29
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 ] ) ;
33
35
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 ) ;
45
46
46
- yAxis = ( g ) =>
47
+ var xAxis = ( g ) =>
47
48
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)' ) ;
57
54
58
55
svg
59
56
. append ( 'rect' )
@@ -69,9 +66,9 @@ function hierarchicalChart(options) {
69
66
70
67
duration = 0 ;
71
68
72
- svg . append ( 'g' ) . call ( yAxis ) ;
69
+ svg . append ( 'g' ) . attr ( 'class' , 'y-axis' ) . call ( yAxis ) ;
73
70
74
- svg . append ( 'g' ) . call ( xAxis ) ;
71
+ svg . append ( 'g' ) . attr ( 'class' , 'x-axis' ) . call ( xAxis ) ;
75
72
76
73
down ( svg , root ) ;
77
74
@@ -83,62 +80,41 @@ function hierarchicalChart(options) {
83
80
// Rebind the current node to the background.
84
81
svg . select ( '.background' ) . datum ( d ) ;
85
82
86
- // Define two sequenced transitions.
87
- const transition1 = svg . transition ( ) . duration ( duration ) ;
88
- const transition2 = transition1 . transition ( ) ;
89
-
90
83
// Mark any currently-displayed bars as exiting.
91
84
const exit = svg . selectAll ( '.enter' ) . attr ( 'class' , 'exit' ) ;
92
85
93
86
// Entering nodes immediately obscure the clicked-on bar, so hide it.
94
87
exit . selectAll ( 'rect' ) . attr ( 'fill-opacity' , ( p ) => ( p === d ? 0 : null ) ) ;
95
88
96
89
// 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 ( ) ;
112
91
113
92
// 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 ) ] ) ;
115
94
116
95
// Update the x-axis.
117
- svg . selectAll ( '.x -axis' ) . transition ( transition2 ) . call ( xAxis ) ;
96
+ svg . selectAll ( '.y -axis' ) . call ( yAxis ) ;
118
97
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 ) ;
124
104
125
105
// Color the bars as parents; they will fade to children if appropriate.
126
106
enter
127
107
. selectAll ( 'rect' )
128
108
. attr ( 'fill' , color ( true ) )
129
109
. attr ( 'fill-opacity' , 1 )
130
- . transition ( transition2 )
131
110
. attr ( 'fill' , ( d ) => color ( ! ! d . children ) )
132
- . attr ( 'width ' , ( d ) => x ( d . value ) - x ( 0 ) ) ;
111
+ . attr ( 'height ' , ( d ) => y ( 0 ) - y ( d . value ) ) ;
133
112
}
134
113
135
114
function bar ( svg , down , d , selector ) {
136
- barStep = ( width - margin . left - margin . right ) / d . children . length ;
137
- barPadding = 0.4 ;
138
115
const g = svg
139
116
. insert ( 'g' , selector )
140
117
. attr ( 'class' , 'enter' )
141
- . attr ( 'transform' , `translate(60,340) rotate(-90)` )
142
118
. attr ( 'text-anchor' , 'end' )
143
119
. style ( 'font' , '10px sans-serif' ) ;
144
120
@@ -149,20 +125,20 @@ function hierarchicalChart(options) {
149
125
. attr ( 'cursor' , ( d ) => ( ! d . children ? null : 'pointer' ) )
150
126
. on ( 'click' , ( event , d ) => ( d . value ? down ( svg , d ) : null ) ) ;
151
127
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 ) ;
160
133
161
- bar
134
+ svg . selectAll ( '.x-axis' ) . call ( xAxis ) ;
135
+
136
+ bar . attr ( 'class' , 'bars' )
162
137
. 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 ] ) ) ;
166
142
167
143
hierTip = options . tip ;
168
144
hierTip
@@ -173,7 +149,7 @@ function hierarchicalChart(options) {
173
149
. style ( 'padding' , '0px 5px 0px 5px' )
174
150
. style ( 'border-radius' , '4px' )
175
151
. style ( 'font-size' , '10px' )
176
- . offset ( [ - 155 , barStep / 4 - barPadding ] )
152
+ . offset ( [ - 10 , 0 ] )
177
153
. html ( function ( event , d ) {
178
154
return '<span>' + d . value + '</span>' ;
179
155
} ) ;
@@ -202,74 +178,58 @@ function hierarchicalChart(options) {
202
178
// Rebind the current node to the background.
203
179
svg . select ( '.background' ) . datum ( d . parent ) ;
204
180
205
- // Define two sequenced transitions.
206
- const transition1 = svg . transition ( ) . duration ( duration ) ;
207
- const transition2 = transition1 . transition ( ) ;
208
-
209
181
// Mark any currently-displayed bars as exiting.
210
182
const exit = svg . selectAll ( '.enter' ) . attr ( 'class' , 'exit' ) ;
211
183
212
184
// 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 ) ] ) ;
214
186
215
187
// Update the x-axis.
216
- svg . selectAll ( '.x -axis' ) . transition ( transition1 ) . call ( xAxis ) ;
188
+ svg . selectAll ( '.y -axis' ) . call ( yAxis ) ;
217
189
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 ) ) ;
223
190
224
191
// Transition exiting rects to the new scale and fade to parent color.
225
192
exit
226
193
. selectAll ( 'rect' )
227
- . transition ( transition1 )
228
- . attr ( 'width' , ( d ) => x ( d . value ) - x ( 0 ) )
194
+ . attr ( 'width' , ( d ) => d3 . min ( [ x . bandwidth ( ) , 150 ] ) )
229
195
. attr ( 'fill' , color ( true ) ) ;
230
196
231
197
// Transition exiting text to fade out.
232
198
// Remove exiting nodes.
233
- exit . transition ( transition2 ) . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
199
+ exit . attr ( 'fill-opacity' , 0 ) . remove ( ) ;
234
200
235
201
// Enter the new bars for the clicked-on data's parent.
236
202
const enter = bar ( svg , down , d . parent , '.exit' ) . attr ( 'fill-opacity' , 0 ) ;
237
203
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 ) ;
242
205
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!
247
206
enter
248
207
. selectAll ( 'rect' )
249
208
. 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 ) )
253
210
. on ( 'end' , function ( p ) {
254
211
d3 . select ( this ) . attr ( 'fill-opacity' , 1 ) ;
255
212
} ) ;
256
213
}
257
214
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
+ }
265
233
}
266
234
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
- }
275
235
}
0 commit comments