solid-gauge.src.js 7.0 KB
/**
 * @license  Highcharts JS v4.2.5 (2016-05-06)
 * Solid angular gauge module
 *
 * (c) 2010-2016 Torstein Honsi
 *
 * License: www.highcharts.com/license
 */

(function (factory) {
	if (typeof module === 'object' && module.exports) {
		module.exports = factory;
	} else {
		factory(Highcharts);
	}
}(function (H) {
	'use strict';

	var defaultPlotOptions = H.getOptions().plotOptions,
		pInt = H.pInt,
		pick = H.pick,
		each = H.each,
		isNumber = H.isNumber,
		colorAxisMethods,
		UNDEFINED;

	// The default options
	defaultPlotOptions.solidgauge = H.merge(defaultPlotOptions.gauge, {
		colorByPoint: true
	});


	// These methods are defined in the ColorAxis object, and copied here.
	// If we implement an AMD system we should make ColorAxis a dependency.
	colorAxisMethods = {


		initDataClasses: function (userOptions) {
			var axis = this,
				chart = this.chart,
				dataClasses,
				colorCounter = 0,
				options = this.options;
			this.dataClasses = dataClasses = [];

			each(userOptions.dataClasses, function (dataClass, i) {
				var colors;

				dataClass = H.merge(dataClass);
				dataClasses.push(dataClass);
				if (!dataClass.color) {
					if (options.dataClassColor === 'category') {
						colors = chart.options.colors;
						dataClass.color = colors[colorCounter++];
						// loop back to zero
						if (colorCounter === colors.length) {
							colorCounter = 0;
						}
					} else {
						dataClass.color = axis.tweenColors(H.Color(options.minColor), H.Color(options.maxColor), i / (userOptions.dataClasses.length - 1));
					}
				}
			});
		},

		initStops: function (userOptions) {
			this.stops = userOptions.stops || [
				[0, this.options.minColor],
				[1, this.options.maxColor]
			];
			each(this.stops, function (stop) {
				stop.color = H.Color(stop[1]);
			});
		},
		/** 
		 * Translate from a value to a color
		 */
		toColor: function (value, point) {
			var pos,
				stops = this.stops,
				from,
				to,
				color,
				dataClasses = this.dataClasses,
				dataClass,
				i;

			if (dataClasses) {
				i = dataClasses.length;
				while (i--) {
					dataClass = dataClasses[i];
					from = dataClass.from;
					to = dataClass.to;
					if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
						color = dataClass.color;
						if (point) {
							point.dataClass = i;
						}
						break;
					}   
				}

			} else {

				if (this.isLog) {
					value = this.val2lin(value);
				}
				pos = 1 - ((this.max - value) / (this.max - this.min));
				i = stops.length;
				while (i--) {
					if (pos > stops[i][0]) {
						break;
					}
				}
				from = stops[i] || stops[i + 1];
				to = stops[i + 1] || from;

				// The position within the gradient
				pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
				
				color = this.tweenColors(
					from.color, 
					to.color,
					pos
				);
			}
			return color;
		},
		/*
		 * Return an intermediate color between two colors, according to pos where 0
		 * is the from color and 1 is the to color.
		 */
		tweenColors: function (from, to, pos) {
			// Check for has alpha, because rgba colors perform worse due to lack of
			// support in WebKit.
			var hasAlpha,
				ret;

			// Unsupported color, return to-color (#3920)
			if (!to.rgba.length || !from.rgba.length) {
				ret = to.input || 'none';

			// Interpolate
			} else {
				from = from.rgba;
				to = to.rgba;
				hasAlpha = (to[3] !== 1 || from[3] !== 1);
				ret = (hasAlpha ? 'rgba(' : 'rgb(') + 
					Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' + 
					Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' + 
					Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) + 
					(hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
			}
			return ret;
		}
	};

	/**
	 * Handle animation of the color attributes directly
	 */
	each(['fill', 'stroke'], function (prop) {
		H.Fx.prototype[prop + 'Setter'] = function () {
			this.elem.attr(prop, colorAxisMethods.tweenColors(H.Color(this.start), H.Color(this.end), this.pos));
		};
	});

	// The series prototype
	H.seriesTypes.solidgauge = H.extendClass(H.seriesTypes.gauge, {
		type: 'solidgauge',
		pointAttrToOptions: {}, // #4301, don't inherit line marker's attribs
		bindAxes: function () {
			var axis;
			H.seriesTypes.gauge.prototype.bindAxes.call(this);

			axis = this.yAxis;
			H.extend(axis, colorAxisMethods);

			// Prepare data classes
			if (axis.options.dataClasses) {
				axis.initDataClasses(axis.options);
			}
			axis.initStops(axis.options);
		},

		/**
		 * Draw the points where each point is one needle
		 */
		drawPoints: function () {
			var series = this,
				yAxis = series.yAxis,
				center = yAxis.center,
				options = series.options,
				renderer = series.chart.renderer,
				overshoot = options.overshoot,
				overshootVal = isNumber(overshoot) ? overshoot / 180 * Math.PI : 0;

			H.each(series.points, function (point) {
				var graphic = point.graphic,
					rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true),
					radius = (pInt(pick(point.options.radius, options.radius, 100)) * center[2]) / 200,
					innerRadius = (pInt(pick(point.options.innerRadius, options.innerRadius, 60)) * center[2]) / 200,
					shapeArgs,
					d,
					toColor = yAxis.toColor(point.y, point),
					axisMinAngle = Math.min(yAxis.startAngleRad, yAxis.endAngleRad),
					axisMaxAngle = Math.max(yAxis.startAngleRad, yAxis.endAngleRad),
					minAngle,
					maxAngle,
					attribs;

				if (toColor === 'none') { // #3708
					toColor = point.color || series.color || 'none';
				}
				if (toColor !== 'none') {
					point.color = toColor;
				}

				// Handle overshoot and clipping to axis max/min
				rotation = Math.max(axisMinAngle - overshootVal, Math.min(axisMaxAngle + overshootVal, rotation));

				// Handle the wrap option
				if (options.wrap === false) {
					rotation = Math.max(axisMinAngle, Math.min(axisMaxAngle, rotation));
				}

				minAngle = Math.min(rotation, yAxis.startAngleRad);
				maxAngle = Math.max(rotation, yAxis.startAngleRad);

				if (maxAngle - minAngle > 2 * Math.PI) {
					maxAngle = minAngle + 2 * Math.PI;
				}

				point.shapeArgs = shapeArgs = {
					x: center[0],
					y: center[1],
					r: radius,
					innerR: innerRadius,
					start: minAngle,
					end: maxAngle,
					fill: toColor
				};
				point.startR = radius; // For PieSeries.animate

				if (graphic) {
					d = shapeArgs.d;
					graphic.animate(shapeArgs);
					if (d) {
						shapeArgs.d = d; // animate alters it
					}
				} else {
					attribs = {
						stroke: options.borderColor || 'none',
						'stroke-width': options.borderWidth || 0,
						fill: toColor,
						'sweep-flag': 0
					};
					if (options.linecap !== 'square') {
						attribs['stroke-linecap'] = attribs['stroke-linejoin'] = 'round';
					}
					point.graphic = renderer.arc(shapeArgs)
						.attr(attribs)
						.add(series.group);
				}
			});
		},

		/**
		 * Extend the pie slice animation by animating from start angle and up
		 */
		animate: function (init) {

			if (!init) {
				this.startAngleRad = this.yAxis.startAngleRad;
				H.seriesTypes.pie.prototype.animate.call(this, init);
			}
		}
	});

}));