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