懒羊羊
2023-08-30 1ac2bc1590406d9babec036e154d8d08f34a6aa1
提交 | 用户 | 时间
1ac2bc 1 /**
2  * @license 
3  * Highcharts funnel module, Beta
4  *
5  * (c) 2010-2012 Torstein Hønsi
6  *
7  * License: www.highcharts.com/license
8  */
9
10 /*global Highcharts */
11 (function (Highcharts) {
12     
13 'use strict';
14
15 // create shortcuts
16 var defaultOptions = Highcharts.getOptions(),
17     defaultPlotOptions = defaultOptions.plotOptions,
18     seriesTypes = Highcharts.seriesTypes,
19     merge = Highcharts.merge,
20     noop = function () {},
21     each = Highcharts.each;
22
23 // set default options
24 defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {
25     center: ['50%', '50%'],
26     width: '90%',
27     neckWidth: '30%',
28     height: '100%',
29     neckHeight: '25%',
30
31     dataLabels: {
32         //position: 'right',
33         connectorWidth: 1,
34         connectorColor: '#606060'
35     },
36     size: true, // to avoid adapting to data label size in Pie.drawDataLabels
37     states: {
38         select: {
39             color: '#C0C0C0',
40             borderColor: '#000000',
41             shadow: false
42         }
43     }    
44 });
45
46
47 seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
48     
49     type: 'funnel',
50     animate: noop,
51
52     /**
53      * Overrides the pie translate method
54      */
55     translate: function () {
56         
57         var 
58             // Get positions - either an integer or a percentage string must be given
59             getLength = function (length, relativeTo) {
60                 return (/%$/).test(length) ?
61                     relativeTo * parseInt(length, 10) / 100 :
62                     parseInt(length, 10);
63             },
64             
65             sum = 0,
66             series = this,
67             chart = series.chart,
68             plotWidth = chart.plotWidth,
69             plotHeight = chart.plotHeight,
70             cumulative = 0, // start at top
71             options = series.options,
72             center = options.center,
73             centerX = getLength(center[0], plotWidth),
74             centerY = getLength(center[0], plotHeight),
75             width = getLength(options.width, plotWidth),
76             tempWidth,
77             getWidthAt,
78             height = getLength(options.height, plotHeight),
79             neckWidth = getLength(options.neckWidth, plotWidth),
80             neckHeight = getLength(options.neckHeight, plotHeight),
81             neckY = height - neckHeight,
82             data = series.data,
83             path,
84             fraction,
85             half = options.dataLabels.position === 'left' ? 1 : 0,
86
87             x1, 
88             y1, 
89             x2, 
90             x3, 
91             y3, 
92             x4, 
93             y5;
94
95         // Return the width at a specific y coordinate
96         series.getWidthAt = getWidthAt = function (y) {
97             return y > height - neckHeight || height === neckHeight ?
98                 neckWidth :
99                 neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight));
100         };
101         series.getX = function (y, half) {
102             return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance);
103         };
104
105         // Expose
106         series.center = [centerX, centerY, height];
107         series.centerX = centerX;
108
109         /*
110          * Individual point coordinate naming:
111          *
112          * x1,y1 _________________ x2,y1
113          *  \                         /
114          *   \                       /
115          *    \                     /
116          *     \                   /
117          *      \                 /
118          *     x3,y3 _________ x4,y3
119          *
120          * Additional for the base of the neck:
121          *
122          *       |               |
123          *       |               |
124          *       |               |
125          *     x3,y5 _________ x4,y5
126          */
127
128
129
130
131         // get the total sum
132         each(data, function (point) {
133             sum += point.y;
134         });
135
136         each(data, function (point) {
137             // set start and end positions
138             y5 = null;
139             fraction = sum ? point.y / sum : 0;
140             y1 = centerY - height / 2 + cumulative * height;
141             y3 = y1 + fraction * height;
142             //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
143             tempWidth = getWidthAt(y1);
144             x1 = centerX - tempWidth / 2;
145             x2 = x1 + tempWidth;
146             tempWidth = getWidthAt(y3);
147             x3 = centerX - tempWidth / 2;
148             x4 = x3 + tempWidth;
149
150             // the entire point is within the neck
151             if (y1 > neckY) {
152                 x1 = x3 = centerX - neckWidth / 2;
153                 x2 = x4 = centerX + neckWidth / 2;
154             
155             // the base of the neck
156             } else if (y3 > neckY) {
157                 y5 = y3;
158
159                 tempWidth = getWidthAt(neckY);
160                 x3 = centerX - tempWidth / 2;
161                 x4 = x3 + tempWidth;
162
163                 y3 = neckY;
164             }
165
166             // save the path
167             path = [
168                 'M',
169                 x1, y1,
170                 'L',
171                 x2, y1,
172                 x4, y3
173             ];
174             if (y5) {
175                 path.push(x4, y5, x3, y5);
176             }
177             path.push(x3, y3, 'Z');
178
179             // prepare for using shared dr
180             point.shapeType = 'path';
181             point.shapeArgs = { d: path };
182
183
184             // for tooltips and data labels
185             point.percentage = fraction * 100;
186             point.plotX = centerX;
187             point.plotY = (y1 + (y5 || y3)) / 2;
188
189             // Placement of tooltips and data labels
190             point.tooltipPos = [
191                 centerX,
192                 point.plotY
193             ];
194
195             // Slice is a noop on funnel points
196             point.slice = noop;
197             
198             // Mimicking pie data label placement logic
199             point.half = half;
200
201             cumulative += fraction;
202         });
203
204
205         series.setTooltipPoints();
206     },
207     /**
208      * Draw a single point (wedge)
209      * @param {Object} point The point object
210      * @param {Object} color The color of the point
211      * @param {Number} brightness The brightness relative to the color
212      */
213     drawPoints: function () {
214         var series = this,
215             options = series.options,
216             chart = series.chart,
217             renderer = chart.renderer;
218
219         each(series.data, function (point) {
220             
221             var graphic = point.graphic,
222                 shapeArgs = point.shapeArgs;
223
224             if (!graphic) { // Create the shapes
225                 point.graphic = renderer.path(shapeArgs).
226                     attr({
227                         fill: point.color,
228                         stroke: options.borderColor,
229                         'stroke-width': options.borderWidth
230                     }).
231                     add(series.group);
232                     
233             } else { // Update the shapes
234                 graphic.animate(shapeArgs);
235             }
236         });
237     },
238
239     /**
240      * Funnel items don't have angles (#2289)
241      */
242     sortByAngle: noop,
243     
244     /**
245      * Extend the pie data label method
246      */
247     drawDataLabels: function () {
248         var data = this.data,
249             labelDistance = this.options.dataLabels.distance,
250             leftSide,
251             sign,
252             point,
253             i = data.length,
254             x,
255             y;
256         
257         // In the original pie label anticollision logic, the slots are distributed
258         // from one labelDistance above to one labelDistance below the pie. In funnels
259         // we don't want this.
260         this.center[2] -= 2 * labelDistance;
261         
262         // Set the label position array for each point.
263         while (i--) {
264             point = data[i];
265             leftSide = point.half;
266             sign = leftSide ? 1 : -1;
267             y = point.plotY;
268             x = this.getX(y, leftSide);
269                 
270             // set the anchor point for data labels
271             point.labelPos = [
272                 0, // first break of connector
273                 y, // a/a
274                 x + (labelDistance - 5) * sign, // second break, right outside point shape
275                 y, // a/a
276                 x + labelDistance * sign, // landing point for connector
277                 y, // a/a
278                 leftSide ? 'right' : 'left', // alignment
279                 0 // center angle
280             ];
281         }
282         
283         seriesTypes.pie.prototype.drawDataLabels.call(this);
284     }
285
286 });
287
288
289 }(Highcharts));