UNPKG

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