@syncfusion/ej2-circulargauge
Version:
Essential JS 2 CircularGauge Components
839 lines (804 loc) • 51.6 kB
text/typescript
import { CircularGauge } from '../circular-gauge';
import { createElement, isNullOrUndefined, AnimationOptions, Animation, animationMode } from '@syncfusion/ej2-base';
import { stringToNumber, measureText, getLocationFromAngle, getLabelFormat, VisibleLabels, isCompleteAngle, Size, GaugeLocation, Rect, getPathArc, getAngleFromValue } from '../utils/helper-common';
import { calculateSum } from '../utils/helper-axis-panel';
import { Axis, Range, Label, Pointer } from './axis';
import { IAxisLabelRenderEventArgs, IRadiusCalculateEventArgs, ICircularGaugeAnimation } from '../model/interface';
import { axisLabelRender, radiusCalculate } from '../model/constants';
import { AxisRenderer } from './axis-renderer';
import { PointerRenderer } from './pointer-renderer';
import { getCompleteArc } from '../utils/helper-pointer-renderer';
import { FontModel } from '../model/base-model';
/**
* Specifies the CircularGauge Axis Layout.
*/
export class AxisLayoutPanel {
private gauge: CircularGauge;
private farSizes: number[];
private axisRenderer: AxisRenderer;
public pointerRenderer: PointerRenderer;
public axisOption : ICircularGaugeAnimation[] = [];
private prevAnimatedMajorTickValue: number[] = [];
private prevAnimatedMajorTickIndex: number[] = [];
private prevAnimatedMinorTickValue: number[] = [];
private prevAnimatedMinorTickIndex: number[] = [];
private prevAnimatedTickType: string;
private allowAxisCount: number[] = [];
private rangeAnimationCount: number = 0;
constructor(gauge: CircularGauge) {
this.gauge = gauge;
this.axisRenderer = new AxisRenderer(gauge);
this.pointerRenderer = new PointerRenderer(gauge);
}
/**
* Measure the calculate the axis size and radius.
*
* @return {void}
* @private
*/
public measureAxis(rect: Rect): void {
this.measureAxisSize(this.gauge, rect);
this.calculateAxesRadius();
}
/**
* Measure to calculate the axis radius of the circular gauge.
*
* @returns {void}
* @private
*/
private calculateAxesRadius(): void {
let totalRadius: number; let currentRadius: number;
let rangeMaximumRadius: number = 0;
const xMarginDiff: number = this.gauge.margin.left + this.gauge.margin.right;
const yMarginDiff: number = this.gauge.margin.top + this.gauge.margin.bottom;
for (const axis of <Axis[]>this.gauge.axes) {
totalRadius = (Math.min(axis.rect.width, axis.rect.height) / 2);
currentRadius = axis.radius != null ? stringToNumber(axis.radius, totalRadius) : totalRadius;
// eslint-disable-next-line prefer-spread
rangeMaximumRadius = Math.max.apply(Math, axis.ranges.map((value: Range) => {
return value.radius ?
(value.radius.indexOf('%') < 0 ? 100 : parseInt(value.radius, 10)) : 0;
}));
currentRadius = (rangeMaximumRadius > 100 && axis.radius == null) ?
(currentRadius * 100) / rangeMaximumRadius : currentRadius;
axis.currentRadius = currentRadius - axis.nearSize;
if (this.gauge.moveToCenter && this.gauge.axes.length === 1 &&
isNullOrUndefined(this.gauge.centerXpoint) && isNullOrUndefined(this.gauge.centerYpoint)) {
let endAngle: number;
const startAngle: number = axis.startAngle;
let startPoint: GaugeLocation = getLocationFromAngle(startAngle - 90, currentRadius, this.gauge.midPoint);
endAngle = axis.endAngle;
endAngle -= isCompleteAngle(startAngle, endAngle) ? 0.0001 : 0;
let endPoint: GaugeLocation = getLocationFromAngle(endAngle - 90, currentRadius, this.gauge.midPoint);
let xDiff: number; let yDiff: number; let startXDiff: number; let endXDiff: number;
let startYDiff: number; let endYDiff: number; let newPoint: GaugeLocation;
if (startAngle > endAngle ? Math.abs(startAngle - endAngle) > 90 ? true : false : true) {
if ((startAngle >= 270 && startAngle <= 360) && ((endAngle > 270 && endAngle <= 360) ||
(endAngle >= 0 && endAngle <= 180))) {
startXDiff = Math.abs(this.gauge.gaugeRect.x - Math.abs(startPoint.x - this.gauge.gaugeRect.x));
newPoint = (endAngle <= 360 && endAngle >= 270) ? this.gauge.midPoint : (endAngle <= 90) ? endPoint :
getLocationFromAngle(90 - 90, currentRadius, this.gauge.midPoint);
endXDiff = Math.abs(newPoint.x - this.gauge.gaugeRect.width);
startPoint = (endAngle <= 360 && endAngle >= 270) ? endPoint :
getLocationFromAngle(360 - 90, currentRadius, this.gauge.midPoint);
startYDiff = Math.abs(startPoint.y - this.gauge.gaugeRect.y);
endPoint = (endAngle <= 360 && endAngle >= 270 || (endAngle >= 0 && endAngle < 90)) ?
this.gauge.midPoint : (endAngle >= 90 && endAngle <= 180) ? endPoint :
getLocationFromAngle(180 - 90, currentRadius, this.gauge.midPoint);
endYDiff = Math.abs(endPoint.y - (this.gauge.gaugeRect.y + this.gauge.gaugeRect.height));
} else if ((startAngle >= 0 && startAngle < 90) && (endAngle >= 0 && endAngle <= 270)) {
startYDiff = Math.abs(startPoint.y - this.gauge.gaugeRect.y);
newPoint = (endAngle >= 180) ? getLocationFromAngle(180 - 90, currentRadius, this.gauge.midPoint) :
endPoint;
endYDiff = Math.abs(newPoint.y - (this.gauge.gaugeRect.y + this.gauge.gaugeRect.height));
startPoint = (endAngle >= 180) ? endPoint : this.gauge.midPoint;
startXDiff = Math.abs(this.gauge.gaugeRect.x - Math.abs(startPoint.x - this.gauge.gaugeRect.x));
endPoint = (endAngle >= 90) ? getLocationFromAngle(90 - 90, currentRadius, this.gauge.midPoint) : endPoint;
endXDiff = Math.abs(endPoint.x - this.gauge.gaugeRect.width);
} else if ((startAngle >= 90 && startAngle < 180) && (endAngle > 90 && endAngle <= 360)) {
newPoint = (endAngle <= 180) ? this.gauge.midPoint : (endAngle >= 270) ?
getLocationFromAngle(270 - 90, currentRadius, this.gauge.midPoint) : endPoint;
startXDiff = Math.abs(newPoint.x - this.gauge.gaugeRect.x);
endXDiff = Math.abs(startPoint.x - this.gauge.gaugeRect.width);
startPoint = (endAngle > 270) ? getLocationFromAngle(endAngle - 90, currentRadius, this.gauge.midPoint) :
this.gauge.midPoint;
startYDiff = Math.abs(this.gauge.gaugeRect.y - startPoint.y);
endPoint = (endAngle >= 180) ? getLocationFromAngle(180 - 90, currentRadius, this.gauge.midPoint) : endPoint;
endYDiff = Math.abs(endPoint.y - (this.gauge.gaugeRect.y + this.gauge.gaugeRect.height));
} else if ((startAngle >= 180 && startAngle <= 270) && ((endAngle <= 360 && endAngle >= 270) ||
(endAngle <= 180 && endAngle >= 0))) {
newPoint = (endAngle > 180 && endAngle < 270) ? endPoint :
getLocationFromAngle(270 - 90, currentRadius, this.gauge.midPoint);
startXDiff = Math.abs(this.gauge.gaugeRect.x - Math.abs(newPoint.x - this.gauge.gaugeRect.x));
newPoint = (endAngle >= 180 && endAngle <= 360) ? this.gauge.midPoint : (endAngle <= 90) ? endPoint :
getLocationFromAngle(0, currentRadius, this.gauge.midPoint);
endXDiff = Math.abs(newPoint.x - this.gauge.gaugeRect.width);
newPoint = (endAngle > 180 && endAngle < 270) ? this.gauge.midPoint : (endAngle >= 270 && endAngle <= 360) ?
endPoint : getLocationFromAngle(360 - 90, currentRadius, this.gauge.midPoint);
startYDiff = Math.abs(newPoint.y - this.gauge.gaugeRect.y);
endPoint = (endAngle <= 360 && endAngle >= 270 || (endAngle >= 0 && endAngle < 90)) ?
startPoint : ((270 - startAngle) < (endAngle - 90)) ? endPoint : startPoint;
endYDiff = Math.abs(endPoint.y - (this.gauge.gaugeRect.y + this.gauge.gaugeRect.height));
}
if ((!isNullOrUndefined(startXDiff) && !isNullOrUndefined(endXDiff) && !isNullOrUndefined(startYDiff) &&
!isNullOrUndefined(endYDiff)) && ((startXDiff > 0 || endXDiff > 0) && (startYDiff > 0 || endYDiff > 0))) {
xDiff = Math.abs((startXDiff + endXDiff) - xMarginDiff);
yDiff = Math.abs((startYDiff + endYDiff) - yMarginDiff);
this.gauge.midPoint.x = this.gauge.midPoint.x - (startXDiff / 2) + (endXDiff / 2);
this.gauge.midPoint.y = this.gauge.midPoint.y - (startYDiff / 2) + (endYDiff / 2);
totalRadius = (Math.min(this.gauge.gaugeRect.width, this.gauge.gaugeRect.height) / 2) +
(Math.min(xDiff, yDiff) / 2);
axis.currentRadius = (axis.radius != null ? stringToNumber(axis.radius, totalRadius) : totalRadius) - axis.nearSize;
}
}
}
axis.visibleRange.interval = this.calculateNumericInterval(axis, axis.rect);
const args: IRadiusCalculateEventArgs = {
cancel: false, name: radiusCalculate, currentRadius: axis.currentRadius, gauge: this.gauge,
midPoint: this.gauge.midPoint, axis: axis
};
this.gauge.trigger('radiusCalculate', args, () => {
axis.currentRadius = args.currentRadius;
this.gauge.midPoint = args.midPoint;
this.calculateVisibleLabels(axis);
});
}
}
/**
* Measure to calculate the axis size.
*
* @return {void}
* @private
*/
private measureAxisSize(gauge: CircularGauge, rect: Rect): void {
let sum: number;
this.computeSize(<Axis[]>gauge.axes, rect);
gauge.axes.map((axis: Axis, index: number) => {
sum = calculateSum(index, this.farSizes.length - 1, this.farSizes);
axis.rect = new Rect(
rect.x + sum, rect.y + sum,
rect.width - (sum * 2), rect.height - (sum * 2)
);
});
}
/**
* Calculate the axis values of the circular gauge.
*
* @return {void}
* @private
*/
private calculateAxisValues(rect?: Rect): void {
for (const axis of <Axis[]>this.gauge.axes) {
this.calculateVisibleRange(axis, rect);
this.calculateVisibleLabels(axis);
}
}
/**
* Calculate the visible range of an axis.
*
* @param {Axis} axis - Specifies the axis.
* @param {Rect} rect - Specifies the rect.
* @returns {void}
* @private
*/
private calculateVisibleRange(axis: Axis, rect: Rect): void {
const interval: number = axis.majorTicks.interval;
let minimumValue: number = Math.min(axis.minimum === null ? 0 : axis.minimum, axis.maximum !== null ? axis.maximum : 100);
let maximumValue: number = Math.max(axis.minimum, axis.maximum === null ? 100 : axis.maximum);
axis.pointers.map((pointer: Pointer) => {
pointer.currentValue = pointer.value !== null ?
pointer.value < minimumValue ? minimumValue : pointer.value > maximumValue ? maximumValue : pointer.value
: minimumValue;
minimumValue = axis.minimum === null ? Math.min(pointer.currentValue, minimumValue) : minimumValue;
maximumValue = axis.maximum === null ? Math.max(pointer.currentValue, maximumValue) : maximumValue;
});
minimumValue = (minimumValue === maximumValue) ?
(interval !== null ? minimumValue - interval : minimumValue - 1) : minimumValue;
axis.visibleRange = { min: minimumValue, max: maximumValue, interval: interval };
axis.visibleRange.interval = this.calculateNumericInterval(axis, rect);
}
/**
* Calculate the numeric intervals of an axis range.
*
* @return {void}
* @private
*/
private calculateNumericInterval(axis: Axis, rect: Rect): number {
const allowComponentRender: boolean = ((!isNullOrUndefined(axis.minimum) && !isNullOrUndefined(axis.maximum)
&& axis.minimum !== axis.maximum) || (isNullOrUndefined(axis.minimum) || isNullOrUndefined(axis.maximum)));
if (!allowComponentRender) {
return 0;
} else if (axis.majorTicks.interval !== null) {
return axis.majorTicks.interval;
}
let totalAngle: number = axis.endAngle - axis.startAngle;
totalAngle = totalAngle <= 0 ? (totalAngle + 360) : totalAngle;
return this.calculateNiceInterval(
axis.visibleRange.max, axis.visibleRange.min,
axis.currentRadius ? axis.currentRadius : (rect.width / 2),
totalAngle
);
}
/**
* Calculate the nice interval of an axis range.
*
* @return {void}
* @private
*/
private calculateNiceInterval(maxValue: number, minValue: number, radius: number, degree: number): number {
const delta: number = maxValue - minValue;
const circumference: number = 2 * Math.PI * radius * (degree / 360);
const desiredIntervalsCount: number = Math.max((circumference * ((0.533 * 3) / 100)), 1);
let niceInterval: number = delta / desiredIntervalsCount;
const minInterval: number = Math.pow(10, Math.floor(Math.log(niceInterval) / Math.log(10)));
for (const interval of [10, 5, 2, 1]) {
const currentInterval: number = minInterval * interval;
if (desiredIntervalsCount < (delta / currentInterval)) {
break;
}
niceInterval = currentInterval;
}
return niceInterval;
}
/**
* Calculate the visible labels of an axis.
*
* @return {void}
* @private
*/
private calculateVisibleLabels(axis: Axis): void {
const style: Label = <Label>axis.labelStyle;
const customLabelFormat: boolean = style.format && style.format.match('{value}') !== null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const format: any = this.gauge.intl.getNumberFormat({
format: getLabelFormat(style.format), useGrouping: this.gauge.useGroupingSeparator
});
let argsData: IAxisLabelRenderEventArgs;
axis.visibleLabels = [];
let roundValue: number;
const interval: number = axis.visibleRange.interval;
const max: number = axis.visibleRange.max;
if ((isNullOrUndefined(axis.minimum) && isNullOrUndefined(axis.maximum)) || axis.minimum !== axis.maximum) {
for (let i: number = axis.visibleRange.min; (i <= max && interval); i += interval) {
roundValue = axis.roundingPlaces ? parseFloat(i.toFixed(axis.roundingPlaces)) : i;
argsData = {
cancel: false, name: axisLabelRender, axis: axis,
text: customLabelFormat ? style.format.replace(new RegExp('{value}', 'g'), format(roundValue)) :
format(roundValue),
value: roundValue
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const axisLabelRenderSuccess: any = (argsData: IAxisLabelRenderEventArgs) => {
if (!argsData.cancel) {
axis.visibleLabels.push(new VisibleLabels(
argsData.text, i
));
}
};
axisLabelRenderSuccess.bind(this);
this.gauge.trigger(axisLabelRender, argsData, axisLabelRenderSuccess);
}
}
const lastLabel: number = axis.visibleLabels.length ? axis.visibleLabels[axis.visibleLabels.length - 1].value : null;
const maxVal: number = axis.visibleRange.max;
if (!isNullOrUndefined(lastLabel) && lastLabel !== maxVal && axis.showLastLabel === true) {
argsData = {
cancel: false, name: axisLabelRender, axis: axis,
text: customLabelFormat ? style.format.replace(new RegExp('{value}', 'g'), format(maxVal)) :
format(maxVal),
value: maxVal
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const axisLabelRenderSuccess: any = (argsData: IAxisLabelRenderEventArgs) => {
if (!argsData.cancel) {
axis.visibleLabels.push(new VisibleLabels(
argsData.text, maxVal
));
}
};
axisLabelRenderSuccess.bind(this);
this.gauge.trigger(axisLabelRender, argsData, axisLabelRenderSuccess);
}
this.getMaxLabelWidth(this.gauge, axis);
}
/**
* Measure the axes available size.
*
* @return {void}
* @private
*/
private computeSize(axes: Axis[], rect: Rect): void {
let lineSize: number;
let outerHeight: number;
let innerHeight: number;
let heightForCross: number;
const axisPadding: number = 5;
let majorTickOffset: number = 0;
let minorTickOffset: number = 0;
let labelOffset: number = 0;
let labelPadding: number = 10;
this.farSizes = [];
this.calculateAxisValues(rect);
for (const axis of axes) {
lineSize = (axis.lineStyle.width / 2);
outerHeight = 0; innerHeight = 0;
heightForCross = axis.majorTicks.position === 'Cross' ? axis.majorTicks.height / 2 : heightForCross;
heightForCross = (axis.minorTicks.position === 'Cross' && heightForCross < axis.minorTicks.height / 2) ?
axis.minorTicks.height / 2 : heightForCross;
heightForCross = (axis.labelStyle.position === 'Cross' && heightForCross < axis.maxLabelSize.height / 2) ?
axis.maxLabelSize.height / 2 : heightForCross;
lineSize = lineSize < heightForCross ? heightForCross : lineSize;
majorTickOffset = axis.majorTicks.offset;
minorTickOffset = axis.minorTicks.offset;
labelOffset = axis.labelStyle.offset;
labelPadding = axis.labelStyle.shouldMaintainPadding ? 10 : 0;
// Calculating the outer space of the axis
outerHeight += !(axis.majorTicks.position === 'Outside' && axis.minorTicks.position === 'Outside' &&
axis.labelStyle.position === 'Outside') ? axisPadding : 0;
outerHeight += (axis.majorTicks.position === 'Outside' ? (axis.majorTicks.height + lineSize) : 0) +
(axis.labelStyle.position === 'Outside' ? (axis.maxLabelSize.height + labelOffset + labelPadding) : 0) +
((axis.minorTicks.position === 'Outside' && !(axis.majorTicks.position === 'Outside')) ?
(axis.minorTicks.height + lineSize) : 0) + lineSize;
outerHeight += (axis.majorTicks.position === 'Outside' && axis.minorTicks.position === 'Outside') ?
Math.max(majorTickOffset, minorTickOffset) : (axis.majorTicks.position === 'Outside' ?
majorTickOffset : axis.minorTicks.position === 'Outside' ? minorTickOffset : 0);
// Calculating the inner space of the axis
innerHeight += ((axis.majorTicks.position === 'Inside') ? (axis.majorTicks.height + lineSize) : 0) +
((axis.labelStyle.position === 'Inside') ? (axis.maxLabelSize.height + labelOffset + labelPadding) : 0) +
((axis.minorTicks.position === 'Inside' && axis.majorTicks.position === 'Outside') ?
(axis.minorTicks.height + lineSize) : 0) + lineSize;
innerHeight += ((axis.majorTicks.position === 'Inside') && (axis.minorTicks.position === 'Inside')) ?
Math.max(majorTickOffset, minorTickOffset) : ((axis.majorTicks.position === 'Inside') ?
majorTickOffset : (axis.minorTicks.position === 'Inside') ? minorTickOffset : 0);
if (this.farSizes[this.farSizes.length - 1]) {
this.farSizes[this.farSizes.length - 1] += (innerHeight + outerHeight);
}
axis.nearSize = outerHeight - axisPadding;
axis.farSize = innerHeight;
outerHeight = (this.gauge.axes.length === (this.farSizes.length + 1)) ? 0 : outerHeight;
this.farSizes.push(outerHeight);
}
}
/**
* To render the Axis element of the circular gauge.
*
* @return {void}
* @private
*/
public renderAxes(animate: boolean = true): void {
const gauge: CircularGauge = this.gauge;
const renderer: AxisRenderer = this.axisRenderer;
let element: Element;
const axesElements: Element = gauge.renderer.createGroup({
'id': gauge.element.id + '_AxesCollection',
'clip-path': 'url(#' + gauge.element.id + '_GaugeAreaClipRect_' + ')'
});
// To append the secondary element for annotation and tooltip
const annotationElement: HTMLElement = createElement('div', {
id: gauge.element.id + '_Secondary_Element'
});
annotationElement.style.position = 'relative';
gauge.element.appendChild(annotationElement);
gauge.axes.map((axis: Axis, index: number) => {
element = gauge.renderer.createGroup({
id: gauge.element.id + '_Axis_Group_' + index
});
this.gauge.allowComponentRender = ((!isNullOrUndefined(axis.minimum) && !isNullOrUndefined(axis.maximum)
&& axis.minimum !== axis.maximum) || (isNullOrUndefined(axis.minimum) || isNullOrUndefined(axis.maximum)));
renderer.checkAngles(axis);
renderer.drawAxisOuterLine(axis, index, element, gauge);
if (gauge.allowRangePreRender) {
renderer.drawAxisRange(axis, index, element);
}
renderer.drawAxisLine(axis, index, element, gauge);
if (!gauge.allowRangePreRender) {
renderer.drawAxisRange(axis, index, element);
}
renderer.drawMajorTickLines(axis, index, element, gauge);
renderer.drawMinorTickLines(axis, index, element, gauge);
renderer.drawAxisLabels(axis, index, element, gauge);
this.pointerRenderer.drawPointers(axis, index, element, gauge, animate);
if (gauge.annotationsModule) {
gauge.annotationsModule.renderAnnotation(axis, index, gauge);
}
axesElements.appendChild(element);
});
// For append clip rect for axes
gauge.svgObject.appendChild(gauge.renderer.drawClipPath({
'id': gauge.element.id + '_GaugeAreaClipRect_',
'x': 0, 'y': 0,
'width': gauge.availableSize.width,
'height': gauge.availableSize.height,
'fill': 'transparent', 'stroke': 'transparent'
}));
gauge.svgObject.appendChild(axesElements);
if (gauge.allowLoadingAnimation) {
this.durationSplitUp((gauge.animationDuration === 0 && animationMode === 'Enable') ? 3000 : gauge.animationDuration, axesElements);
}
}
private labelElementAnimation(element: HTMLElement, axisIndex: number): void {
if (element) {
new Animation({}).animate(<HTMLElement>element, {
duration: this.gauge.loadingAnimationDuration[axisIndex as number],
progress: (): void => {
element.style.visibility = 'visible';
},
end: (): void => {
element.style.visibility = 'visible';
const axisElement: Element = document.getElementById(this.gauge.element.id + '_Axis_Labels_' + axisIndex);
if (this.gauge.axes[axisIndex as number].showLastLabel && parseInt(element.id.split('Label_')[1], 10) === (axisElement.childElementCount - 2)) {
(axisElement as HTMLElement).style.visibility = 'visible';
element = document.getElementById(this.gauge.element.id + '_Axis_' + axisIndex + '_Label_' + (axisElement.childElementCount - 1));
if (element) {
element.style.visibility = 'visible';
}
}
}
});
}
}
private elementLabelAnimation(element: HTMLElement, axisIndex: number, tickIndex: number, gauge: CircularGauge): void {
if (element) {
new Animation({}).animate(<HTMLElement>element, {
duration: gauge.axes[axisIndex as number].labelStyle.font.size != null &&
(gauge.axes[axisIndex as number].labelStyle.font.size === '0px' || gauge.axes[axisIndex as number].labelStyle.font.size === '0') ? 0 :
((gauge.loadingAnimationDuration[axisIndex as number] / this.axisOption[axisIndex as number].axisLabelCount)),
progress: (): void => {
element.style.visibility = 'visible';
},
end: (): void => {
tickIndex += 1;
this.elementLabelAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex),
axisIndex, tickIndex, gauge);
const axisElement: Element = document.getElementById(this.gauge.element.id + '_Axis_Labels_' + axisIndex);
if (this.gauge.axes[axisIndex as number].showLastLabel && parseInt(element.id.split('Label_')[1], 10) === (axisElement.childElementCount - 2)) {
element = document.getElementById(this.gauge.element.id + '_Axis_' + axisIndex + '_Label_' + (axisElement.childElementCount - 1));
if (element) {
element.style.visibility = 'visible';
}
(axisElement as HTMLElement).style.visibility = 'visible';
}
if (this.rangeAnimationCount === 0 && (this.axisOption[axisIndex as number].axisLabelCount - 1) === tickIndex) {
(axisElement as HTMLElement).style.visibility = 'visible';
this.rangeAnimationCount++;
this.rangeAnimation(gauge);
}
}
});
}
}
public axisLineCalculation(axisElement: HTMLElement, axis: Axis, value: number, gauge: CircularGauge): void {
const checkMinValue: boolean = value === axis.visibleRange.min;
const location: GaugeLocation = gauge.midPoint;
const isClockWise: boolean = axis.direction === 'ClockWise';
const axisWidth: number = axis.lineStyle.width / 2;
let startAngle: number = getAngleFromValue(
axis.visibleRange.min, axis.visibleRange.max, axis.visibleRange.min,
axis.startAngle, axis.endAngle, isClockWise
);
let endAngle: number = getAngleFromValue(
value, axis.visibleRange.max, axis.visibleRange.min,
axis.startAngle, axis.endAngle, isClockWise
);
if (isClockWise) {
if (startAngle > endAngle) {
endAngle = Math.round(startAngle) === Math.round(endAngle) && !checkMinValue ?
Math.round(endAngle) - 0.5 : Math.round(endAngle);
}
else {
endAngle = startAngle === endAngle && !checkMinValue ? endAngle + 1 : endAngle;
}
} else {
endAngle = Math.round(startAngle) === Math.round(endAngle) && !checkMinValue ?
[startAngle, startAngle = (endAngle > startAngle ? endAngle + 0.5 : endAngle - 1)][0]
: [startAngle, startAngle = endAngle][0];
}
axisElement.setAttribute('d', getCompleteArc(
location, startAngle, endAngle, (axis.currentRadius + axisWidth), (axis.currentRadius - axisWidth),
checkMinValue
));
}
public axisLineAnimation(axisIndex: number, duration: number, gauge: CircularGauge): void {
// eslint-disable-next-line
const axis: Axis = (gauge.axes[axisIndex] as Axis);
this.prevAnimatedMajorTickValue.push(axis.minimum);
this.prevAnimatedMinorTickValue.push(axis.minimum);
this.prevAnimatedMinorTickIndex.push(0);
this.prevAnimatedMajorTickIndex.push(0);
this.prevAnimatedTickType = 'major';
if (this.axisOption[axisIndex as number].isAxisLine) {
const axisElement: HTMLElement = document.getElementById(gauge.element.id + '_AxisLine_' + axisIndex);
const start: number = axis.visibleRange.min;
const end: number = axis.visibleRange.max;
let pointerValue: number;
let timeStamp: number;
const val: number = Math.abs(start - end);
new Animation({}).animate(axisElement, {
duration: duration,
progress: (arg: AnimationOptions): void => {
axisElement.style.visibility = 'visible';
axisElement.setAttribute('fill', axis.lineStyle.color);
axisElement.setAttribute('stroke-width', '0');
timeStamp = (arg.timeStamp / arg.duration);
pointerValue = end > start ? start + (timeStamp * val) : start - (timeStamp * val);
this.axisLineCalculation(axisElement, axis, pointerValue, gauge);
},
end: () => {
axisElement.setAttribute('fill', 'transparent');
axisElement.setAttribute('stroke-width', axis.lineStyle.width.toString());
axisElement.setAttribute('d', getPathArc(gauge.midPoint, axis.startAngle - 90, axis.endAngle - 90, axis.currentRadius));
axisElement.style.visibility = 'visible';
this.axisAnimation(axisIndex, duration, gauge);
}
});
} else if (this.axisOption[axisIndex as number].isMajorTick || this.axisOption[axisIndex as number].isMinorTick) {
if (this.axisOption[axisIndex as number].isMajorTick || (this.axisOption[axisIndex as number].isMajorTick &&
this.axisOption[axisIndex as number].isMinorTick)) {
this.tickElementAnimation(document.getElementById(gauge.element.id + '_Axis_Major_TickLine_' + axisIndex + '_' + 0),
document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 0), duration,
axisIndex, this.axisOption[axisIndex as number].isMajorTick &&
this.axisOption[axisIndex as number].isMinorTick
? 0 : -1, 'major', this.axisOption[axisIndex as number], gauge);
} else if (this.axisOption[axisIndex as number].isMinorTick) {
this.tickElementAnimation(document.getElementById(gauge.element.id + '_Axis_Minor_TickLine_' + axisIndex + '_' + 0),
document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 0), duration,
axisIndex, -1, 'minor', this.axisOption[axisIndex as number], gauge);
}
} else if (!this.axisOption[axisIndex as number].isAxisLine) {
this.labelRangeAnimation(gauge, axisIndex);
}
}
public axisAnimation(axisIndex: number, duration: number, gauge: CircularGauge): void {
const axisElement: HTMLElement = document.getElementById(gauge.element.id + '_AxisLine_' + axisIndex);
const axisOption: ICircularGaugeAnimation = this.axisOption[axisIndex as number];
new Animation({}).animate(axisElement, {
duration: (this.axisOption[axisIndex as number].majorTickCount === 0 ? 0
: duration / this.axisOption[axisIndex as number].majorTickCount),
progress: (): void => {
axisElement.style.visibility = 'visible';
},
end: () => {
if (axisOption.isMajorTick) {
this.tickElementAnimation(document.getElementById(gauge.element.id + '_Axis_Major_TickLine_' + axisIndex + '_' + 0),
document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 0), duration,
axisIndex, axisOption.isMajorTick && axisOption.isMinorTick ? 0 : -1, 'major', axisOption, gauge);
} else if (axisOption.isMinorTick) {
this.tickElementAnimation(document.getElementById(gauge.element.id + '_Axis_Minor_TickLine_' + axisIndex + '_' + 0),
document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 0), duration,
axisIndex, -1, 'minor', axisOption, gauge);
}
this.labelRangeAnimation(gauge, axisIndex);
}
});
}
private tickElementAnimation(tickElement: HTMLElement, labelElement: HTMLElement, duration: number, axisIndex: number,
// eslint-disable-next-line
tickIndex: number, type: string, axis: any, gauge: CircularGauge): void {
if (tickElement && this.gauge.isAnimationProgress) {
new Animation({}).animate(<HTMLElement>tickElement, {
duration: (axis.isMinorTick ? axis.minorTickCount === 0 ? 0
: (duration / axis.minorTickCount) / this.allowAxisCount[axisIndex as number] :
axis.majorTickCount === 0 ? 0 : (duration / axis.majorTickCount) / this.allowAxisCount[axisIndex as number]),
progress: (): void => {
tickElement.style.visibility = 'visible';
},
end: (): void => {
if (axis.isMajorTick && axis.isMinorTick && gauge.allowLoadingAnimation && this.gauge.isAnimationProgress) {
tickElement.style.visibility = 'visible';
const currentTickValue: number = parseFloat(tickElement.getAttribute('data-interval'));
this.prevAnimatedTickType = type;
if (type === 'major') {
this.prevAnimatedMajorTickValue[axisIndex as number] = currentTickValue;
this.prevAnimatedMajorTickIndex[axisIndex as number] = tickIndex;
} else {
this.prevAnimatedMinorTickValue[axisIndex as number] = currentTickValue;
this.prevAnimatedMinorTickIndex[axisIndex as number] = tickIndex;
}
const minorTickInterval: number = (gauge.axes[axisIndex as number].minorTicks.interval != null
? gauge.axes[axisIndex as number].minorTicks.interval :
((gauge.axes[axisIndex as number] as Axis).visibleRange.interval / 2));
const minorTickValue: number = minorTickInterval < (gauge.axes[axisIndex as number] as
Axis).visibleRange.interval ? currentTickValue +
minorTickInterval : this.prevAnimatedMinorTickValue[axisIndex as number] + minorTickInterval;
const majorTickValue: number = this.prevAnimatedMajorTickValue[axisIndex as number]
+ (gauge.axes[axisIndex as number] as Axis).visibleRange.interval;
type = minorTickValue < majorTickValue ? 'minor' : 'major';
if (type === 'major' && axis.majorTickCount !== axis.minorTickCount && tickIndex !== 0 && this.prevAnimatedTickType === 'minor') {
tickIndex = this.prevAnimatedMajorTickIndex[axisIndex as number];
}
if (type === 'minor' && axis.majorTickCount !== axis.minorTickCount && tickIndex !== 0 && this.prevAnimatedTickType === 'major') {
tickIndex = this.prevAnimatedMinorTickIndex[axisIndex as number];
}
tickIndex = type === 'minor' ? axis.majorTickCount === axis.minorTickCount ? tickIndex : (currentTickValue ===
this.prevAnimatedMajorTickValue[axisIndex as number] ? tickIndex : tickIndex + 1) : tickIndex + 1;
tickElement = type === 'minor' ? document.getElementById(gauge.element.id + '_Axis_Minor_TickLine_' + axisIndex + '_' + tickIndex) :
document.getElementById(gauge.element.id + '_Axis_Major_TickLine_' + axisIndex + '_' + tickIndex);
labelElement = type === 'minor' ? null : document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex);
if (type === 'major' || tickIndex === 0) {
this.labelElementAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex), axisIndex);
}
this.tickElementAnimation(tickElement, labelElement, duration, axisIndex, tickIndex, type, axis, gauge);
if (this.rangeAnimationCount === 0 && type === 'minor' && (tickIndex === axis.minorTickCount - 1 || tickIndex === axis.minorTickCount) && (gauge.axes.length - 1) === axisIndex) {
this.rangeAnimationCount++;
this.rangeAnimation(gauge);
}
} else if (gauge.allowLoadingAnimation && axis.isMajorTick && this.gauge.isAnimationProgress) {
tickElement.style.visibility = 'visible';
type = 'major';
tickIndex = tickIndex + 1;
tickElement = document.getElementById(gauge.element.id + '_Axis_Major_TickLine_' + axisIndex + '_' + tickIndex);
labelElement = document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex);
this.tickElementAnimation(tickElement, labelElement, duration, axisIndex, tickIndex, type, axis, gauge);
if (type === 'major' || tickIndex === 0) {
this.labelElementAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex), axisIndex);
}
if (this.rangeAnimationCount === 0 && type === 'major' && tickIndex === axis.majorTickCount - 1 && (gauge.axes.length - 1) === axisIndex) {
this.rangeAnimationCount++;
this.rangeAnimation(gauge);
}
} else if (gauge.allowLoadingAnimation && this.gauge.isAnimationProgress && axis.isMinorTick) {
tickElement.style.visibility = 'visible';
type = 'minor';
tickIndex = tickIndex + 1;
tickElement = document.getElementById(gauge.element.id + '_Axis_Minor_TickLine_' + axisIndex + '_' + tickIndex);
labelElement = document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex);
this.tickElementAnimation(tickElement, labelElement, duration, axisIndex, tickIndex, type, axis, gauge);
if (type === 'minor') {
this.labelElementAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + tickIndex), axisIndex);
}
if (this.rangeAnimationCount === 0 && type === 'minor' && tickIndex === axis.minorTickCount - 1 && (gauge.axes.length - 1) === axisIndex) {
this.rangeAnimationCount++;
this.rangeAnimation(gauge);
}
}
}
});
}
this.labelRangeAnimation(gauge, axisIndex);
}
private labelRangeAnimation(gauge: CircularGauge, axisIndex: number): void {
const options: ICircularGaugeAnimation = this.axisOption[axisIndex as number] as ICircularGaugeAnimation;
if (!isNullOrUndefined(options)) {
if (!options.isMajorTick && !options.isMinorTick && options.isAxisLabel) {
if (options.axisLabelCount > 0) {
if (gauge.axes[axisIndex as number].labelStyle.hiddenLabel === 'First') {
this.elementLabelAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 1),
axisIndex, 0, gauge);
} else {
this.elementLabelAnimation(document.getElementById(gauge.element.id + '_Axis_' + axisIndex + '_Label_' + 0),
axisIndex, 0, gauge);
}
}
} else if ((this.rangeAnimationCount === 0 && !options.isMajorTick &&
!options.isMinorTick && !options.isAxisLabel && options.isRange)
|| this.rangeAnimationCount === 0 && this.gauge.isAnimationProgress
&& !options.isMajorTick && !options.isMinorTick && !options.isAxisLabel && options.isRange) {
this.rangeAnimationCount++;
this.rangeAnimation(gauge);
}
}
}
private rangeAnimation(gauge: CircularGauge): void {
for (let j: number = 0; j < gauge.axes.length; j++) {
const rangesElement: Element = document.getElementById(gauge.element.id + '_Axis_Ranges_' + j);
if (!isNullOrUndefined(rangesElement) && gauge.allowLoadingAnimation) {
this.rangeElementAnimation(rangesElement, j, gauge);
}
}
}
private rangeElementAnimation(rangeElement: Element, axisIndex: number, gauge: CircularGauge): void {
let height: number = 0;
const opacity: number = 1;
const isRangeAbsent: boolean = rangeElement.childElementCount > 0 &&
gauge.axes[axisIndex as number].ranges[0].start !== gauge.axes[axisIndex as number].ranges[0].end;
new Animation({}).animate(<HTMLElement>rangeElement, {
duration: isRangeAbsent ? gauge.loadingAnimationDuration[axisIndex as number] : 0,
progress: (args: AnimationOptions): void => {
if (args.timeStamp > args.delay) {
height = ((args.timeStamp - args.delay) / args.duration);
rangeElement['style']['opacity'] = (opacity * height);
}
},
end: (): void => {
rangeElement['style']['opacity'] = opacity;
const axisElement: Element = document.getElementById(this.gauge.element.id + '_Axis_Labels_' + axisIndex);
if (!isNullOrUndefined(axisElement)) {
(axisElement as HTMLElement).style.visibility = 'visible';
}
if (gauge.allowLoadingAnimation && axisIndex === 0) {
this.axisOption = [];
gauge.axes.map((axis: Axis, axisindex: number) => {
axis.pointers.map((pointer: Pointer, pointerIndex: number) => {
const pointerElement: HTMLElement = document.getElementById(gauge.element.id + '_Axis_' + axisindex + '_Pointer_' + pointerIndex);
if (!isNullOrUndefined(pointerElement) && this.gauge.isAnimationProgress) {
pointer.previousValue = !this.gauge.isPropertyChange ? axis.minimum : pointer.previousValue;
gauge.gaugeAxisLayoutPanel.pointerRenderer.doPointerAnimation(pointerElement, pointer, axis, axisIndex);
}
});
if (axis.pointers.length === 0 && this.gauge.isAnimationProgress) {
if (this.gauge.loadingAnimationDuration[axisIndex as number] > 0 &&
!isNullOrUndefined(this.gauge.annotationsModule)) {
this.gauge.annotationsModule.annotationAnimation(this.gauge);
}
} else {
this.gauge.isOverAllAnimationComplete = true;
}
});
}
}
});
}
private durationSplitUp(duration: number, axesElements: Element): void {
let splitUpCount: number = 0;
this.gauge.loadingAnimationDuration = [];
for (let i: number = 0; i < axesElements.childElementCount; i++) {
splitUpCount = 0;
let axisCount: number = 0;
const element: Element = axesElements.children[i as number] as Element;
let isAxisLine: boolean = false; let isMajorTick: boolean = false; let majorTickCount: number = 0;
let labelCount: number = 0; let isMinorTick: boolean = false; let minorTickCount: number = 0;
let isLabel: boolean = false; let isrange: boolean = false; let isPointer: boolean = false;
for (let j: number = 0; j < element.childElementCount; j++) {
const elementId: string = element.children[j as number]['id'];
if (elementId.indexOf('_AxisLine_') > 0) {
isAxisLine = true;
splitUpCount++;
} else if (elementId.indexOf('MajorTickLines') > 0) {
isMajorTick = true;
axisCount++;
majorTickCount = element.children[j as number].childElementCount;
splitUpCount++;
} else if (elementId.indexOf('MinorTickLines') > 0) {
isMinorTick = true;
axisCount++;
minorTickCount = element.children[j as number].childElementCount;
if (!isMajorTick) { splitUpCount++; }
} else if (elementId.indexOf('_Axis_Labels_') > 0) {
isLabel = true;
axisCount++;
labelCount = element.children[j as number].childElementCount;
if (!isMajorTick && !isMinorTick && this.gauge.axes[i as number].labelStyle.font.size != null &&
(this.gauge.axes[i as number].labelStyle.font.size !== '0px' && this.gauge.axes[i as number].labelStyle.font.size !== '0')) { splitUpCount++; }
} else if (elementId.indexOf('_Axis_Ranges_') > 0) {
isrange = true;
if (this.gauge.axes[i as number].ranges.length === 1
&& (!isNullOrUndefined(this.gauge.axes[i as number].ranges)
&& this.gauge.axes[i as number].ranges[0].start === 0
&& this.gauge.axes[i as number].ranges[0].end === 0)) {
splitUpCount++;
}
} else if (elementId.indexOf('_Axis_Pointers_') > 0) {
isPointer = true;
if (this.gauge.axes[i as number].pointers.length > 0 && this.gauge.axes[i as number].pointers.length !== 1 &&
this.gauge.axes[i as number].pointers[0].value !== this.gauge.axes[i as number].minimum) {
splitUpCount++;
}
}
}
this.allowAxisCount.push(axisCount === 0 ? 1 : axisCount);
this.axisOption.push({
isAxisLine: isAxisLine, isMajorTick: isMajorTick, isMinorTick: isMinorTick,
isAxisLabel: isLabel, isPointer: isPointer, isRange: isrange,
axisLabelCount: labelCount, majorTickCount: majorTickCount, minorTickCount: minorTickCount
});
isAxisLine = false; isMajorTick = false; majorTickCount = 0;
isMinorTick = false; labelCount = 0; minorTickCount = 0;
isLabel = false; isrange = false; isPointer = false;
if (this.gauge.axes[i as number].annotations != null
&& this.gauge.axes[i as number].annotations.length > 0
&& !isNullOrUndefined(this.gauge.annotationsModule)) {
splitUpCount++;
}
this.gauge.loadingAnimationDuration.push(splitUpCount === 0 ? duration : duration / splitUpCount);
}
}
/**
* Calculate maximum label width for the axis.
*
* @param {CircularGauge} gauge - Specifies the instance of the gauge.
* @param {Axis} axis - Specifies the axis.
* @returns {void}
*/
private getMaxLabelWidth(gauge: CircularGauge, axis: Axis): void {
axis.maxLabelSize = new Size(0, 0);
co