lib/RadialGauge.js
/*!
* The MIT License (MIT)
*
* Copyright (c) 2016 Mykhailo Stadnyk <[email protected]>
*
* 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;