lib/RadialGauge.js
- /*!
- * The MIT License (MIT)
- *
- * Copyright (c) 2016 Mykhailo Stadnyk <mikhus@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- require('./polyfill');
-
- const GenericOptions = require('./GenericOptions');
- const BaseGauge = require('./BaseGauge');
- const SmartCanvas = require('./SmartCanvas');
- const drawings = require('./drawings');
-
- const PI = Math.PI;
- const HPI = PI / 2;
-
- /**
- * Gauge configuration options
- *
- * @typedef {GenericOptions | {
- * exactTicks: boolean,
- * ticksAngle: number,
- * startAngle: number,
- * colorNeedleCircleOuter: string,
- * colorNeedleCircleOuterEnd: string,
- * colorNeedleCircleInner: string,
- * colorNeedleCircleInnerEnd: string,
- * needleCircleSize: number,
- * needleCircleInner: boolean,
- * needleCircleOuter: boolean,
- * animationTarget: string,
- * useMinPath: boolean,
- * barStartPosition: 'right' | 'left',
- * }} RadialGaugeOptions
- */
-
- /**
- * Default gauge configuration options
- *
- * @access private
- * @type {RadialGaugeOptions}
- */
- const defaultRadialGaugeOptions = Object.assign({}, GenericOptions, {
- // basic options
- ticksAngle: 270,
- startAngle: 45,
-
- // colors
- colorNeedleCircleOuter: '#f0f0f0',
- colorNeedleCircleOuterEnd: '#ccc',
- colorNeedleCircleInner: '#e8e8e8',
- colorNeedleCircleInnerEnd: '#f5f5f5',
-
- // needle
- needleCircleSize: 10,
- needleCircleInner: true,
- needleCircleOuter: true,
- needleStart: 20,
-
- // custom animations
- animationTarget: 'needle', // 'needle' or 'plate'
- useMinPath: false,
-
- barWidth: 0,
- barStartPosition: 'left'
- });
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gradient-filled circle on a canvas
- *
- * @access private
- * @param {number} radius
- * @param {number} width
- * @param {Canvas2DContext} context
- * @param {string} start gradient start color
- * @param {string} end gradient end color
- */
- function drawRadialBorder(radius, width, context, start, end) {
- context.beginPath();
- //noinspection JSUnresolvedFunction
- context.arc(0, 0, abs(radius), 0, PI * 2, true);
- context.lineWidth = width;
- context.strokeStyle = end ?
- drawings.linearGradient(context, start, end, radius) :
- start;
- context.stroke();
- context.closePath();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Returns max radius without borders for the gauge
- *
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- * @return {number}
- */
- function maxRadialRadius(context, options) {
- let pxRatio = SmartCanvas.pixelRatio;
-
- if (!context.maxRadius) {
- context.maxRadius = context.max
- - options.borderShadowWidth
- - options.borderOuterWidth * pxRatio
- - options.borderMiddleWidth * pxRatio
- - options.borderInnerWidth * pxRatio
- + (options.borderOuterWidth ? 0.5 : 0)
- + (options.borderMiddleWidth ? 0.5 : 0)
- + (options.borderInnerWidth ? 0.5 : 0);
- }
-
- return context.maxRadius;
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge plate on the canvas
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialPlate(context, options) {
- let pxRatio = SmartCanvas.pixelRatio;
- let d0 = options.borderShadowWidth * pxRatio;
- let r0 = context.max - d0 - (options.borderOuterWidth * pxRatio) / 2;
- let r1 = r0 - (options.borderOuterWidth * pxRatio) / 2 -
- (options.borderMiddleWidth * pxRatio) / 2 + 0.5;
- let r2 = r1 - (options.borderMiddleWidth * pxRatio) / 2 -
- (options.borderInnerWidth * pxRatio) / 2 + 0.5;
- let r3 = maxRadialRadius(context, options);
- let grad;
- let shadowDrawn = false;
-
- context.save();
-
- if (options.borderOuterWidth) {
- shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
- drawRadialBorder(r0,
- options.borderOuterWidth * pxRatio,
- context,
- options.colorBorderOuter,
- options.colorBorderOuterEnd);
- }
-
- if (options.borderMiddleWidth) {
- shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
- drawRadialBorder(r1,
- options.borderMiddleWidth * pxRatio,
- context,
- options.colorBorderMiddle,
- options.colorBorderMiddleEnd);
- }
-
- if (options.borderInnerWidth) {
- shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
- drawRadialBorder(r2,
- options.borderInnerWidth * pxRatio,
- context,
- options.colorBorderInner,
- options.colorBorderInnerEnd);
- }
-
- drawings.drawShadow(context, options, shadowDrawn);
-
- context.beginPath();
- //noinspection JSUnresolvedFunction
- context.arc(0, 0, abs(r3), 0, PI * 2, true);
-
- if (options.colorPlateEnd) {
- grad = context.createRadialGradient(0, 0, r3 / 2, 0, 0, r3);
- grad.addColorStop(0, options.colorPlate);
- grad.addColorStop(1, options.colorPlateEnd);
- }
-
- else {
- grad = options.colorPlate;
- }
-
- context.fillStyle = grad;
-
- context.fill();
- context.closePath();
-
- context.restore();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge highlight areas on a canvas
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialHighlights(
- context,
- options
- ) {
- let hlWidth = context.max *
- (parseFloat(options.highlightsWidth) || 0) / 100;
-
- if (!hlWidth) return;
-
- //noinspection JSUnresolvedFunction
- let r = abs(radialTicksRadius(context, options) - hlWidth / 2);
- let i = 0, s = options.highlights.length;
- let vd = (options.maxValue - options.minValue) / options.ticksAngle;
-
- context.save();
-
- for (; i < s; i++) {
- let hlt = options.highlights[i];
-
- context.beginPath();
-
- context.rotate(HPI);
- context.arc(0, 0, r,
- drawings.radians(options.startAngle +
- (hlt.from - options.minValue) / vd),
- drawings.radians(options.startAngle +
- (hlt.to - options.minValue) / vd),
- false
- );
- context.strokeStyle = hlt.color;
- context.lineWidth = hlWidth;
- context.lineCap = options.highlightsLineCap;
- context.stroke();
- context.closePath();
-
- context.restore();
- context.save();
- }
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws minor ticks bar on a canvas
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialMinorTicks(context, options) {
- let radius = radialTicksRadius(context, options);
- let s, range, angle;
- let i = 0;
- let delta = 0;
- let minTicks = Math.abs(options.minorTicks) || 0;
- let ratio = options.ticksAngle / (options.maxValue - options.minValue);
-
- context.lineWidth = SmartCanvas.pixelRatio;
- context.strokeStyle = options.colorMinorTicks || options.colorStrokeTicks;
-
- context.save();
-
- if (options.exactTicks) {
- range = options.maxValue - options.minValue;
- s = minTicks ? range / minTicks : 0;
- delta = (BaseGauge.mod(options.majorTicks[0], minTicks) || 0) * ratio;
- }
-
- else {
- s = minTicks * (options.majorTicks.length - 1);
- }
-
- for (; i < s; ++i) {
- angle = options.startAngle + delta + i * (options.ticksAngle / s);
- if (angle <= (options.ticksAngle + options.startAngle )) {
- context.rotate(drawings.radians(angle));
-
- context.beginPath();
- context.moveTo(0, radius);
- context.lineTo(0, radius - context.max * 0.075);
- closeStrokedPath(context);
- }
- }
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Returns ticks radius
- *
- * @access private
- * @param context
- * @param options
- * @return {number}
- */
- function radialTicksRadius(context, options) {
- let unit = context.max / 100;
-
- return maxRadialRadius(context, options) - 5 * unit -
- (options.barWidth ?
- ((parseFloat(options.barStrokeWidth) || 0) * 2 +
- ((parseFloat(options.barWidth) || 0) + 5) * unit) :
- 0);
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge major ticks bar on a canvas
- *
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialMajorTicks(context, options) {
- drawings.prepareTicks(options);
-
- //noinspection JSUnresolvedFunction
- let r = abs(radialTicksRadius(context, options));
- let i, colors;
- let s = options.majorTicks.length;
- let pixelRatio = SmartCanvas.pixelRatio;
-
- context.lineWidth = 2 * pixelRatio;
- context.save();
-
- colors = options.colorMajorTicks instanceof Array ?
- options.colorMajorTicks : new Array(s).fill(options.colorStrokeTicks ||
- options.colorMajorTicks);
-
- i = 0;
- for (; i < s; ++i) {
- context.strokeStyle = colors[i];
- context.rotate(drawings.radians(radialNextAngle(
- options,
- options.exactTicks ? options.majorTicks[i] : i,
- s
- )));
-
- context.beginPath();
- context.moveTo(0, r);
- context.lineTo(0, r - context.max * 0.15);
- closeStrokedPath(context);
- }
-
- if (options.strokeTicks) {
- context.strokeStyle = options.colorStrokeTicks || colors[0];
- context.rotate(HPI);
-
- context.beginPath();
- context.arc(0, 0, r,
- drawings.radians(options.startAngle),
- drawings.radians(options.startAngle + options.ticksAngle),
- false
- );
- closeStrokedPath(context);
- }
- }
-
- /* istanbul ignore next: private, not testable */
- function radialNextAngle(options, i, s) {
- if (options.exactTicks) {
- let ratio = options.ticksAngle / (options.maxValue - options.minValue);
- return options.startAngle + ratio * (i - options.minValue);
- }
-
- return options.startAngle + i * (options.ticksAngle / (s - 1));
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Strokes, closes path and restores previous context state
- *
- * @param {Canvas2DContext} context
- */
- function closeStrokedPath(context) {
- context.stroke();
- context.restore();
- context.closePath();
- context.save();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge bar numbers
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialNumbers(context, options) {
- let radius = radialTicksRadius(context, options) - context.max * 0.15;
- let points = {};
- let i = 0;
- let s = options.majorTicks.length;
- let isAnimated = options.animationTarget !== 'needle';
- let colors = options.colorNumbers instanceof Array ?
- options.colorNumbers : new Array(s).fill(options.colorNumbers);
-
- let plateValueAngle = isAnimated ? -(options.value - options.minValue) /
- (options.maxValue - options.minValue) * options.ticksAngle : 0;
-
- if (isAnimated) {
- context.save();
- context.rotate(-drawings.radians(plateValueAngle));
- }
-
- context.font = drawings.font(options, 'Numbers', context.max / 200);
- context.lineWidth = 0;
- context.textAlign = 'center';
- context.textBaseline = 'middle';
-
- for (; i < s; ++i) {
- let angle = plateValueAngle + radialNextAngle(options,
- options.exactTicks ? options.majorTicks[i] : i, s);
- let textWidth = context.measureText(options.majorTicks[i]).width;
- let textHeight = options.fontNumbersSize;
- let textRadius = Math.sqrt(textWidth * textWidth +
- textHeight * textHeight) / 2;
- let point = drawings.radialPoint(radius - textRadius -
- options.numbersMargin / 100 * context.max,
- drawings.radians(angle));
-
- if (angle === 360) angle = 0;
-
- if (points[angle]) {
- continue; //already drawn at this place, skipping
- }
-
- points[angle] = true;
-
- context.fillStyle = colors[i];
- context.fillText(options.majorTicks[i], point.x, point.y);
- }
-
- isAnimated && context.restore();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge title
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialTitle(context, options) {
- if (!options.title) return;
-
- context.save();
- context.font = drawings.font(options, 'Title', context.max / 200);
- context.fillStyle = options.colorTitle;
- context.textAlign = 'center';
- context.fillText(options.title, 0, -context.max / 4.25, context.max * 0.8);
- context.restore();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws units name on the gauge
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialUnits(context, options) {
- if (!options.units) return;
-
- context.save();
- context.font = drawings.font(options, 'Units', context.max / 200);
- context.fillStyle = options.colorUnits;
- context.textAlign = 'center';
- context.fillText(
- drawings.formatContext(options, options.units),
- 0,
- context.max / 3.25,
- context.max * 0.8);
- context.restore();
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge needle
- *
- * @access private
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialNeedle(context, options) {
- if (!options.needle) return;
-
- let value = options.ticksAngle < 360 ?
- drawings.normalizedValue(options).indented : options.value;
- let startAngle = isFixed ? options.startAngle :
- (options.startAngle + (value - options.minValue) /
- (options.maxValue - options.minValue) * options.ticksAngle);
- if (options.barStartPosition === 'right') {
- startAngle = options.startAngle + options.ticksAngle -
- (value - options.minValue) / (options.maxValue - options.minValue) *
- options.ticksAngle;
- }
- let max = maxRadialRadius(context, options);
- //noinspection JSUnresolvedFunction
- let r1 = abs(max / 100 * options.needleCircleSize);
- //noinspection JSUnresolvedFunction
- let r2 = abs(max / 100 * options.needleCircleSize * 0.75);
- //noinspection JSUnresolvedFunction
- let rIn = abs(max / 100 * options.needleEnd);
- //noinspection JSUnresolvedFunction
- let rStart = abs(options.needleStart ?
- max / 100 * options.needleStart : 0);
- //noinspection JSUnresolvedFunction
- let pad1 = max / 100 * options.needleWidth;
- let pad2 = max / 100 * options.needleWidth / 2;
- let pixelRatio = SmartCanvas.pixelRatio;
- let isFixed = options.animationTarget !== 'needle';
-
- context.save();
-
- drawings.drawNeedleShadow(context, options);
-
- context.rotate(drawings.radians(startAngle));
-
- context.fillStyle = drawings.linearGradient(
- context,
- options.colorNeedle,
- options.colorNeedleEnd,
- rIn - rStart);
-
- if (options.needleType === 'arrow') {
- context.beginPath();
- context.moveTo(-pad2, -rStart);
- context.lineTo(-pad1, 0);
- context.lineTo(-1 * pixelRatio, rIn);
- context.lineTo(pixelRatio, rIn);
- context.lineTo(pad1, 0);
- context.lineTo(pad2, -rStart);
- context.closePath();
- context.fill();
-
- context.beginPath();
- context.lineTo(-0.5 * pixelRatio, rIn);
- context.lineTo(-1 * pixelRatio, rIn);
- context.lineTo(-pad1, 0);
- context.lineTo(-pad2, -rStart);
- context.lineTo(pad2 / 2 * pixelRatio - 2 * pixelRatio, -rStart);
- context.closePath();
- context.fillStyle = options.colorNeedleShadowUp;
- context.fill();
- }
-
- else { // simple line needle
- context.beginPath();
- context.moveTo(-pad2, rIn);
- context.lineTo(-pad2, rStart);
- context.lineTo(pad2, rStart);
- context.lineTo(pad2, rIn);
- context.closePath();
- context.fill();
- }
-
- if (options.needleCircleSize) {
- context.restore();
-
- drawings.drawNeedleShadow(context, options);
-
- if (options.needleCircleOuter) {
- context.beginPath();
- context.arc(0, 0, r1, 0, PI * 2, true);
- context.fillStyle = drawings.linearGradient(
- context,
- options.colorNeedleCircleOuter,
- options.colorNeedleCircleOuterEnd,
- r1
- );
- context.fill();
- context.closePath();
- }
-
- if (options.needleCircleInner) {
- context.beginPath();
- context.arc(0, 0, r2, 0, PI * 2, true);
- context.fillStyle = drawings.linearGradient(
- context,
- options.colorNeedleCircleInner,
- options.colorNeedleCircleInnerEnd,
- r2
- );
- context.fill();
- context.closePath();
- }
-
- context.restore();
- }
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge value box
- *
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- * @param {number} value
- */
- function drawRadialValueBox(
- context,
- options,
- value
- ) {
- drawings.drawValueBox(context, options, value, 0,
- context.max - context.max * 0.33, context.max);
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Computes start and end angle depending on barStartPositionOption
- *
- * @param {RadialGaugeOptions} options
- */
- function computeAngles(options) {
- let sa = options.startAngle;
- let ea = options.startAngle + options.ticksAngle;
- let startAngle = sa;
- let endAngle = sa +
- (drawings.normalizedValue(options).normal -
- options.minValue) / (options.maxValue - options.minValue) *
- options.ticksAngle;
- if (options.barStartPosition === 'middle') {
- let midValue = (options.minValue + options.maxValue) * 0.5;
- if (options.value < midValue) {
- startAngle = 180 - ((
- (midValue - drawings.normalizedValue(options).normal) /
- (options.maxValue - options.minValue) *
- options.ticksAngle
- ));
- endAngle = 180;
- } else {
- startAngle = 180;
- endAngle = 180 + ((
- (drawings.normalizedValue(options).normal - midValue) /
- (options.maxValue - options.minValue) *
- options.ticksAngle));
- }
- } else if (options.barStartPosition === 'right') {
- startAngle = ea - endAngle + sa;
- endAngle = ea;
- }
- return {startAngle, endAngle};
- }
-
- /* istanbul ignore next: private, not testable */
- /**
- * Draws gauge progress bar
- *
- * @param {Canvas2DContext} context
- * @param {RadialGaugeOptions} options
- */
- function drawRadialProgressBar(context, options) {
- let unit = context.max / 100;
- let rMax = maxRadialRadius(context, options) - 5 * unit;
- let sw = (parseFloat(options.barStrokeWidth + '') || 0);
- let w = (parseFloat(options.barWidth + '') || 0) * unit;
- let rMin = rMax - sw * 2 - w;
- let half = (rMax- rMin) / 2;
- let r = rMin + half;
- let delta = sw / r;
- let sa = options.startAngle;
- let ea = options.startAngle + options.ticksAngle;
-
- context.save();
- context.rotate(HPI);
-
- if (sw) {
- // draw stroke
- context.beginPath();
- context.arc(0, 0, r, drawings.radians(sa) - delta,
- drawings.radians(ea) + delta, false);
- context.strokeStyle = options.colorBarStroke;
- context.lineWidth = half * 2;
- context.stroke();
- context.closePath();
- }
-
- if (w) {
- // draw bar
- context.beginPath();
- context.arc(0, 0, r, drawings.radians(sa), drawings.radians(ea), false);
- context.strokeStyle = options.colorBar;
- context.lineWidth = w;
- context.stroke();
- context.closePath();
-
- if (options.barShadow) {
- // draw shadow
- context.beginPath();
- context.arc(0, 0, rMax, drawings.radians(sa), drawings.radians(ea),
- false);
- context.clip();
-
- context.beginPath();
- context.strokeStyle = options.colorBar;
- context.lineWidth = 1;
- context.shadowBlur = options.barShadow;
- context.shadowColor = options.colorBarShadow;
- context.shadowOffsetX = 0;
- context.shadowOffsetY = 0;
- context.arc(0, 0, rMax,
- drawings.radians(options.startAngle),
- drawings.radians(options.startAngle + options.ticksAngle),
- false);
- context.stroke();
- context.closePath();
-
- context.restore();
- context.rotate(HPI);
- }
-
- // draw bar progress
- if (options.barProgress) {
- let angles = computeAngles(options);
- let startAngle = angles.startAngle;
- let endAngle = angles.endAngle;
-
- context.beginPath();
- context.arc(0, 0, r,
- drawings.radians(startAngle),
- drawings.radians(endAngle),
- false);
- context.strokeStyle = options.colorBarProgress;
- context.lineWidth = w;
- context.stroke();
- context.closePath();
- }
- }
-
- context.restore();
- }
-
- /**
- * Find and return gauge value to display
- *
- * @param {RadialGauge} gauge
- */
- function displayValue(gauge) {
- if (gauge.options.animatedValue) {
- return gauge.options.value;
- }
-
- return gauge.value;
- }
-
- /**
- * Minimalistic HTML5 Canvas Gauge
- * @example
- * var gauge = new RadialGauge({
- * renderTo: 'gauge-id', // identifier of HTML canvas element or element itself
- * width: 400,
- * height: 400,
- * units: 'Km/h',
- * title: false,
- * value: 0,
- * minValue: 0,
- * maxValue: 220,
- * majorTicks: [
- * '0','20','40','60','80','100','120','140','160','180','200','220'
- * ],
- * minorTicks: 2,
- * strokeTicks: false,
- * highlights: [
- * { from: 0, to: 50, color: 'rgba(0,255,0,.15)' },
- * { from: 50, to: 100, color: 'rgba(255,255,0,.15)' },
- * { from: 100, to: 150, color: 'rgba(255,30,0,.25)' },
- * { from: 150, to: 200, color: 'rgba(255,0,225,.25)' },
- * { from: 200, to: 220, color: 'rgba(0,0,255,.25)' }
- * ],
- * colorPlate: '#222',
- * colorMajorTicks: '#f5f5f5',
- * colorMinorTicks: '#ddd',
- * colorTitle: '#fff',
- * colorUnits: '#ccc',
- * colorNumbers: '#eee',
- * colorNeedleStart: 'rgba(240, 128, 128, 1)',
- * colorNeedleEnd: 'rgba(255, 160, 122, .9)',
- * valueBox: true,
- * animationRule: 'bounce'
- * });
- * // draw initially
- * gauge.draw();
- * // animate
- * setInterval(() => {
- * gauge.value = Math.random() * -220 + 220;
- * }, 1000);
- */
- export default class RadialGauge extends BaseGauge {
-
- /**
- * Fired each time before gauge plate is drawn
- *
- * @event RadialGauge#beforePlate
- */
-
- /**
- * Fired each time before gauge highlight areas are drawn
- *
- * @event RadialGauge#beforeHighlights
- */
-
- /**
- * Fired each time before gauge minor ticks are drawn
- *
- * @event RadialGauge#beforeMinorTicks
- */
-
- /**
- * Fired each time before gauge major ticks are drawn
- *
- * @event RadialGauge#beforeMajorTicks
- */
-
- /**
- * Fired each time before gauge tick numbers are drawn
- *
- * @event RadialGauge#beforeNumbers
- */
-
- /**
- * Fired each time before gauge title is drawn
- *
- * @event RadialGauge#beforeTitle
- */
-
- /**
- * Fired each time before gauge units text is drawn
- *
- * @event RadialGauge#beforeUnits
- */
-
- /**
- * Fired each time before gauge progress bar is drawn
- *
- * @event RadialGauge#beforeProgressBar
- */
-
- /**
- * Fired each time before gauge value box is drawn
- *
- * @event RadialGauge#beforeValueBox
- */
-
- /**
- * Fired each time before gauge needle is drawn
- *
- * @event RadialGauge#beforeNeedle
- */
-
- /**
- * @constructor
- * @param {RadialGaugeOptions} options
- */
- constructor(options) {
- options = Object.assign({}, defaultRadialGaugeOptions, options || {});
- super(RadialGauge.configure(options));
- }
-
- /**
- * Checks and updates gauge options properly
- *
- * @param {*} options
- * @return {*}
- * @access protected
- */
- static configure(options) {
- if (options.barWidth > 50) options.barWidth = 50;
-
- /* istanbul ignore if */
- if (isNaN(options.startAngle)) options.startAngle = 45;
- /* istanbul ignore if */
- if (isNaN(options.ticksAngle)) options.ticksAngle = 270;
-
- /* istanbul ignore if */
- if (options.ticksAngle > 360) options.ticksAngle = 360;
- /* istanbul ignore if */
- if (options.ticksAngle < 0) options.ticksAngle = 0;
-
- /* istanbul ignore if */
- if (options.startAngle < 0) options.startAngle = 0;
- /* istanbul ignore if */
- if (options.startAngle > 360) options.startAngle = 360;
-
- return options;
- }
-
- /**
- * Sets the value for radial gauge
- *
- * @param {number} value
- */
- set value(value) {
- value = BaseGauge.ensureValue(value, this.options.minValue);
-
- if (this.options.animation &&
- this.options.ticksAngle === 360 &&
- this.options.useMinPath
- ) {
- this._value = value;
- value = this.options.value +
- ((((value - this.options.value) % 360) + 540) % 360) - 180;
- }
-
- super.value = value;
- }
-
- /**
- * Returns current gauge value
- *
- * @return {number}
- */
- get value() {
- return super.value;
- }
-
- /**
- * Triggering gauge render on a canvas.
- *
- * @returns {RadialGauge}
- */
- draw() {
- try {
- let canvas = this.canvas;
- let [x, y, w, h] = [
- -canvas.drawX,
- -canvas.drawY,
- canvas.drawWidth,
- canvas.drawHeight
- ];
- let options = this.options;
-
- if (options.animationTarget === 'needle') {
- if (!canvas.elementClone.initialized) {
- let context = canvas.contextClone;
-
- // clear the cache
- context.clearRect(x, y, w, h);
- context.save();
-
- this.emit('beforePlate');
- drawRadialPlate(context, options);
- this.emit('beforeHighlights');
- drawRadialHighlights(context, options);
- this.emit('beforeMinorTicks');
- drawRadialMinorTicks(context, options);
- this.emit('beforeMajorTicks');
- drawRadialMajorTicks(context, options);
- this.emit('beforeNumbers');
- drawRadialNumbers(context, options);
- this.emit('beforeTitle');
- drawRadialTitle(context, options);
- this.emit('beforeUnits');
- drawRadialUnits(context, options);
-
- canvas.elementClone.initialized = true;
- }
-
- this.canvas.commit();
-
- // clear the canvas
- canvas.context.clearRect(x, y, w, h);
- canvas.context.save();
-
- canvas.context.drawImage(canvas.elementClone, x, y, w, h);
- canvas.context.save();
-
- this.emit('beforeProgressBar');
- drawRadialProgressBar(canvas.context, options);
- this.emit('beforeValueBox');
- drawRadialValueBox(canvas.context, options, displayValue(this));
- this.emit('beforeNeedle');
- drawRadialNeedle(canvas.context, options);
- }
-
- else {
- let plateValueAngle = -drawings.radians(
- (options.value - options.minValue) /
- (options.maxValue - options.minValue) *
- options.ticksAngle);
-
- // clear the canvas
- canvas.context.clearRect(x, y, w, h);
- canvas.context.save();
-
- this.emit('beforePlate');
- drawRadialPlate(canvas.context, options);
-
- canvas.context.rotate(plateValueAngle);
-
- // animated
- this.emit('beforeHighlights');
- drawRadialHighlights(canvas.context, options);
- this.emit('beforeMinorTicks');
- drawRadialMinorTicks(canvas.context, options);
- this.emit('beforeMajorTicks');
- drawRadialMajorTicks(canvas.context, options);
- this.emit('beforeNumbers');
- drawRadialNumbers(canvas.context, options);
- this.emit('beforeProgressBar');
- drawRadialProgressBar(canvas.context, options);
-
- // non-animated
- canvas.context.rotate(-plateValueAngle);
- canvas.context.save();
-
- if (!canvas.elementClone.initialized) {
- let context = canvas.contextClone;
-
- // clear the cache
- context.clearRect(x, y, w, h);
- context.save();
-
- this.emit('beforeTitle');
- drawRadialTitle(context, options);
- this.emit('beforeUnits');
- drawRadialUnits(context, options);
- this.emit('beforeNeedle');
- drawRadialNeedle(context, options);
-
- canvas.elementClone.initialized = true;
- }
-
- canvas.context.drawImage(canvas.elementClone, x, y, w, h);
- }
-
- // value box animations
- this.emit('beforeValueBox');
- drawRadialValueBox(canvas.context, options, displayValue(this));
-
- super.draw();
- }
-
- catch (err) {
- drawings.verifyError(err);
- }
-
- return this;
- }
- }
-
-
- /**
- * @ignore
- * @typedef {object} ns
- */
- /* istanbul ignore if */
- if (typeof ns !== 'undefined') {
- ns['RadialGauge'] = RadialGauge;
- }
-
- BaseGauge.initialize('RadialGauge', defaultRadialGaugeOptions);
-
- module.exports = RadialGauge;