1
+ /*
2
+ * @Author : funlee
3
+
4
+ * @Date : 2017-12-26 22:39:06
5
+ * @Last Modified time: 2017-12-26 22:39:06
6
+ * @Description : 绘制饼图
7
+ */
8
+ import * as d3 from 'd3'
9
+ export default class MultiPie {
10
+ /**
11
+ * 默认配置项
12
+ * @return {[type] } [description]
13
+ */
14
+ defaultSetting ( ) {
15
+ return {
16
+ width : 900 ,
17
+ height : 270 ,
18
+ radius : [ 50 , 66 ] , // [innerRadius,outerRadius]
19
+ gap : 100 , // 相邻两个图形的间距
20
+ margin : { // 多个图形布局:从左往右,竖直方向按容器高度居中放置,故只设置左侧距离left即可
21
+ left : 10
22
+ } ,
23
+ label : { // 名称文本样式
24
+ normal : {
25
+ fontSize : 32 ,
26
+ color : '#46aaff' ,
27
+ anchor : 'middle' ,
28
+ cursor : 'pointer' ,
29
+ top : 46 // 名称文本距离图案顶部的距离
30
+ } ,
31
+ emphasis : {
32
+ fontSize : 32 ,
33
+ color : '#74ffd3' ,
34
+ anchor : 'middle' ,
35
+ cursor : 'pointer' ,
36
+ top : 46 // 名称文本距离图案顶部的距离
37
+ }
38
+ } ,
39
+ itemStyle : {
40
+ label : { // value值文本样式
41
+ fontSize : 32 ,
42
+ color : '#46aaff' ,
43
+ anchor : 'middle' ,
44
+ cursor : 'pointer' ,
45
+ top : 10 // value值文本距离容器中线的偏移距离,默认放在饼图正中间
46
+ } ,
47
+ color : [ // 饼图填充色
48
+ [ '#4a8ce5' , 'black' ] ,
49
+ [ '#44ff86' , 'black' ] ,
50
+ [ '#dccc5c' , 'black' ]
51
+ ]
52
+ }
53
+ }
54
+ }
55
+ /**
56
+ * 初始化,创建容器
57
+ * @param {String } selector 图表容器,支持class或id
58
+ * @param {Object } option 配置项,控制图形样式
59
+ * @return {[type] } [description]
60
+ */
61
+ constructor ( selector , option ) {
62
+ const defaultSetting = this . defaultSetting ( )
63
+ this . config = Object . assign ( defaultSetting , option )
64
+ const { width, height } = this . config
65
+ // 创建svg
66
+ this . svg = d3 . select ( selector )
67
+ . append ( 'svg' )
68
+ . attr ( {
69
+ 'width' : width ,
70
+ 'height' : height
71
+ } )
72
+ }
73
+ /**
74
+ * 处理原始数据,获取pie布局转换后的数据
75
+ * @param {Array } data 原始数据
76
+ * @return {Array } dataset 转换后的数据
77
+ */
78
+ getDataset ( data ) {
79
+ let dataset = [ ]
80
+ const clockwisePie = d3 . layout . pie ( ) // 顺时针,针对数据类型:[small,bigger]
81
+ const anticlockwisePie = d3 . layout . pie ( ) // 逆时针,针对数据类型:[bigger,small]
82
+ . startAngle ( 0 )
83
+ . endAngle ( - 2 * Math . PI )
84
+ // 求取总数:sum
85
+ let sum = 0
86
+ data . map ( d => {
87
+ sum += parseInt ( d . value , 10 )
88
+ } )
89
+ data . map ( ( d , i ) => {
90
+ let value = d . value
91
+ let rate = Math . max ( Math . floor ( value * 100 / sum ) , 1 )
92
+ let rateData = [ rate , 100 - rate ]
93
+ let dealData = rate >= 50 ? clockwisePie ( rateData ) : anticlockwisePie ( rateData )
94
+ dataset . push ( dealData )
95
+ } )
96
+ return dataset
97
+ }
98
+ /**
99
+ * 绘制图案底部的名称文本
100
+ * @param {Object } chart 包裹文本的外层g容器
101
+ * @param {Object } info 单组原始数据,包括name和value
102
+ * @return {[type] } [description]
103
+ */
104
+ renderName ( chart , info ) {
105
+ const {
106
+ radius : [ innerRadius , outerRadius ] ,
107
+ label : {
108
+ normal : {
109
+ fontSize : fontSizeNor ,
110
+ color : colorNor ,
111
+ anchor : anchorNor ,
112
+ top : topNor ,
113
+ cursor : cursorNor
114
+ } ,
115
+ emphasis : {
116
+ fontSize : fontSizeEmp ,
117
+ color : colorEmp ,
118
+ anchor : anchorEmp ,
119
+ top : topEmp ,
120
+ cursor : cursorEmp
121
+ }
122
+ }
123
+ } = this . config
124
+ chart . select ( '.pie-name' ) . attr ( {
125
+ 'font-size' : fontSizeNor ,
126
+ 'fill' : colorNor ,
127
+ 'text-anchor' : anchorNor ,
128
+ 'transform' : `translate(0, ${ outerRadius + topNor } )` ,
129
+ 'cursor' : cursorNor
130
+ } )
131
+ . text ( info . name )
132
+ . on ( 'mouseover' , function ( ) {
133
+ d3 . select ( this ) . attr ( {
134
+ 'font-size' : fontSizeEmp ,
135
+ 'fill' : colorEmp ,
136
+ 'text-anchor' : anchorEmp ,
137
+ 'transform' : `translate(0, ${ outerRadius + topEmp } )` ,
138
+ 'cursor' : cursorNor
139
+ } )
140
+ } )
141
+ . on ( 'mouseout' , function ( ) {
142
+ d3 . select ( this ) . attr ( {
143
+ 'font-size' : fontSizeNor ,
144
+ 'fill' : colorNor ,
145
+ 'text-anchor' : anchorNor ,
146
+ 'transform' : `translate(0, ${ outerRadius + topNor } )` ,
147
+ 'cursor' : cursorNor
148
+ } )
149
+ } )
150
+ }
151
+ /**
152
+ * 绘制图案中间的value值文本
153
+ * @param {Object } chart 包裹文本的外层g容器
154
+ * @param {[type] } info 单组原始数据,包括name和value
155
+ * @return {[type] } [description]
156
+ */
157
+ renderValue ( chart , info ) {
158
+ const { itemStyle : { label : { fontSize, color, anchor, cursor, top } } } = this . config
159
+ chart . select ( '.pie-value' ) . attr ( {
160
+ 'font-size' : fontSize ,
161
+ 'fill' : color ,
162
+ 'text-anchor' : anchor ,
163
+ 'transform' : `translate(0,${ top } )` ,
164
+ 'cursor' : cursor
165
+ } )
166
+ . text ( info . value )
167
+ }
168
+ /**
169
+ * 绘制单个Pie图案
170
+ * @param {Objec } chartName 单个图案的外层g容器
171
+ * @param {Array } pieData 绘制饼图的数据(已经过布局处理)
172
+ * @param {Object } info 该图案的原始数据,包括name和value
173
+ * @param {Array } color 填充饼图的两个颜色值
174
+ * @return {[type] } [description]
175
+ */
176
+ creatPie ( chartName , pieData , info , color ) {
177
+ const { radius : [ innerRadius , outerRadius ] } = this . config
178
+ const arc = d3 . svg . arc ( )
179
+ . innerRadius ( innerRadius )
180
+ . outerRadius ( outerRadius )
181
+ const chart = this . svg . select ( chartName )
182
+ const update = chart . selectAll ( 'path' ) . data ( pieData )
183
+ const enter = update . enter ( )
184
+ update . exit ( ) . remove ( )
185
+ // 绘制饼图图案
186
+ enter . append ( 'path' )
187
+ chart . selectAll ( 'path' ) . data ( pieData )
188
+ . attr ( {
189
+ 'fill' : ( d , i ) => {
190
+ return color [ i ]
191
+ } ,
192
+ 'd' : ( d , i ) => {
193
+ return arc ( d )
194
+ }
195
+ } )
196
+ // 绘制名称--name
197
+ enter . append ( 'text' ) . attr ( {
198
+ 'class' : 'pie-name'
199
+ } )
200
+ this . renderName ( chart , info )
201
+
202
+ // 绘制value值
203
+ enter . append ( 'text' ) . attr ( {
204
+ 'class' : 'pie-value'
205
+ } )
206
+ this . renderValue ( chart , info )
207
+ }
208
+ render ( data ) {
209
+ let dataset = this . getDataset ( data )
210
+ const update = this . svg . selectAll ( '.item' )
211
+ . data ( dataset )
212
+ const enter = update . enter ( ) . append ( 'g' ) . attr ( {
213
+ 'class' : 'item'
214
+ } )
215
+ update . exit ( ) . remove ( )
216
+ // 多个图形布局:从左往右,相邻图形间隔为配置项----config.gap
217
+ const { height, radius : [ , R ] , gap, margin : { left } , itemStyle : { label, color } } = this . config
218
+ const group = this . svg . selectAll ( '.item' ) . data ( dataset )
219
+ . attr ( {
220
+ 'transform' : ( d , i ) => {
221
+ let x = R + left + 2 * R * i + i * gap
222
+ return `translate(${ x } ,${ height / 2 } )`
223
+ } ,
224
+ 'class' : ( d , i ) => {
225
+ return `item${ i } item`
226
+ }
227
+ } )
228
+ // 逐个绘制饼图
229
+ dataset . map ( ( d , i ) => {
230
+ this . creatPie ( `.item${ i } ` , d , data [ i ] , color [ i ] )
231
+ } )
232
+ }
233
+ }
0 commit comments