@syncfusion/ej2-charts
Version:
Feature-rich chart control with built-in support for over 25 chart types, technical indictors, trendline, zooming, tooltip, selection, crosshair and trackball.
459 lines (458 loc) • 23.3 kB
JavaScript
import { getAnimationFunction, pathAnimation, getElement, animateAddPoints, markerAnimate } from '../../common/utils/helper';
import { Rect } from '@syncfusion/ej2-svg-base';
import { Animation, animationMode, isNullOrUndefined } from '@syncfusion/ej2-base';
/**
* Base class for line-type series.
* This class provides common properties and methods for line-type series in the chart.
*
* @private
*/
var LineBase = /** @class */ (function () {
/**
* Initializes the tooltip module for the chart.
*
* @param {Chart} [chartModule] - The chart instance to which the tooltip module is initialized.
*/
function LineBase(chartModule) {
this.chart = chartModule;
}
/**
* Enhances the performance of the chart by enabling complex properties.
*
* @param {Series} series - The series for which complex properties are enabled.
* @returns {Points[]} An array of points.
* @private
*/
LineBase.prototype.enableComplexProperty = function (series) {
var tempPoints = [];
var tempPoints2 = [];
var xVisibleRange = series.xAxis.visibleRange;
var yVisibleRange = series.yAxis.visibleRange;
var seriesPoints = series.points;
var areaBounds = series.clipRect;
var xTolerance = this.chart && this.chart.zoomRedraw && this.chart.redraw ? this.previousX :
Math.abs(xVisibleRange.delta / areaBounds.width);
var yTolerance = this.chart && this.chart.zoomRedraw && this.chart.redraw ? this.previousY :
Math.abs(yVisibleRange.delta / areaBounds.height);
var prevXValue = (seriesPoints[0] && seriesPoints[0].xValue > xTolerance) ? 0 : xTolerance;
var prevYValue = (seriesPoints[0] && seriesPoints[0].y > yTolerance) ? 0 : yTolerance;
this.previousX = xTolerance;
this.previousY = yTolerance;
var xVal = 0;
var yVal = 0;
for (var _i = 0, seriesPoints_1 = seriesPoints; _i < seriesPoints_1.length; _i++) {
var currentPoint = seriesPoints_1[_i];
currentPoint.symbolLocations = [];
xVal = !isNullOrUndefined(currentPoint.xValue) ? currentPoint.xValue : xVisibleRange.min;
yVal = !isNullOrUndefined(currentPoint.yValue) ? currentPoint.yValue : yVisibleRange.min;
if (Math.abs(prevXValue - xVal) >= xTolerance || Math.abs(prevYValue - yVal) >= yTolerance) {
tempPoints.push(currentPoint);
prevXValue = xVal;
prevYValue = yVal;
}
}
var tempPoint;
for (var i = 0; i < tempPoints.length; i++) {
tempPoint = tempPoints[i];
if (isNullOrUndefined(tempPoint.x) || (series.category === 'Indicator' && (isNaN(tempPoint.xValue) || isNaN(tempPoint.yValue)))) {
continue;
}
else {
tempPoints2.push(tempPoint);
}
}
return tempPoints2;
};
/**
* To generate the line path direction.
*
* @param {Points} firstPoint firstPoint
* @param {Points} secondPoint secondPoint
* @param {Series} series series
* @param {boolean} isInverted isInverted
* @param {Function} getPointLocation getPointLocation
* @param {string} startPoint startPoint
* @returns {string} get line path direction
* @private
*/
LineBase.prototype.getLineDirection = function (firstPoint, secondPoint, series, isInverted, getPointLocation, startPoint) {
var direction = '';
if (firstPoint != null) {
var point1 = getPointLocation(firstPoint.xValue, firstPoint.yValue, series.xAxis, series.yAxis, isInverted, series);
var point2 = getPointLocation(secondPoint.xValue, secondPoint.yValue, series.xAxis, series.yAxis, isInverted, series);
direction = startPoint + ' ' + (point1.x) + ' ' + (point1.y) + ' ' +
'L' + ' ' + (point2.x) + ' ' + (point2.y) + ' ';
}
return direction;
};
/**
* Appends a line path to the chart.
*
* @param {PathOption} options - The options for the path.
* @param {Series} series - The series to which the path belongs.
* @param {string} clipRect - The clipping rectangle for the path.
* @returns {void}
* @private
*/
LineBase.prototype.appendLinePath = function (options, series, clipRect) {
var points = this.appendPathElement(options, series, clipRect);
pathAnimation(points.element, options.d, series.chart.redraw, points.previousDirection, points.chart.duration);
};
LineBase.prototype.appendPathElement = function (options, series, clipRect) {
var element = getElement(options.id);
var chart = series.chart;
var previousDirection = element ? element.getAttribute('d') : null;
var htmlObject = series.chart.renderer.drawPath(options, new Int32Array([series.clipRect.x, series.clipRect.y]));
if (htmlObject) {
htmlObject.setAttribute('clip-path', clipRect);
}
if (series.category === 'TrendLine' && htmlObject) {
var trendline = chart.series[series.sourceIndex].trendlines[series.index];
if (!trendline.marker.visible) {
htmlObject.setAttribute('tabindex', trendline.accessibility.focusable ? String(trendline.accessibility.tabIndex) : '-1');
}
htmlObject.setAttribute('role', trendline.accessibility.accessibilityRole ? trendline.accessibility.accessibilityRole : '');
htmlObject.setAttribute('aria-label', trendline.accessibility.accessibilityDescription ? trendline.accessibility.accessibilityDescription : '');
}
series.pathElement = htmlObject;
if (!series.chart.enableCanvas) {
series.seriesElement.appendChild(htmlObject);
}
series.isRectSeries = false;
return { element: element, previousDirection: previousDirection, chart: chart };
};
/**
* Adds a line path to equate the start and end paths.
*
* @param {PathOption} options - The options for the path.
* @param {Series} series - The series to which the path belongs.
* @param {string} clipRect - The clip rectangle for the path.
* @returns {void}
* @private
*/
LineBase.prototype.addPath = function (options, series, clipRect) {
var points = this.appendPathElement(options, series, clipRect);
if (points.previousDirection !== '' && options.d !== '') {
var startPathCommands = points.previousDirection.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/g);
var endPathCommands = (options.d).match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/g);
var maxLength = Math.max(startPathCommands.length, endPathCommands.length);
var minLength = Math.min(startPathCommands.length, endPathCommands.length);
if (startPathCommands.length < endPathCommands.length) {
for (var i = minLength; i < maxLength; i++) {
if (endPathCommands.length !== startPathCommands.length) {
startPathCommands.push((startPathCommands[startPathCommands.length - 1]).replace('M', 'L'));
}
}
animateAddPoints(points.element, options.d, series.chart.redraw, startPathCommands.join(' '), this.chart.duration);
}
else if (startPathCommands.length > endPathCommands.length) {
for (var i = minLength; i < maxLength; i++) {
if (endPathCommands.length !== startPathCommands.length) {
if (series.removedPointIndex === series.points.length) {
endPathCommands.push((endPathCommands[endPathCommands.length - 1]).replace('M', 'L'));
}
else {
endPathCommands.splice(1, 0, endPathCommands[0].replace('M', 'L'));
}
}
}
animateAddPoints(points.element, endPathCommands.join(''), series.chart.redraw, points.previousDirection, this.chart.duration, options.d);
}
else {
animateAddPoints(points.element, options.d, series.chart.redraw, points.previousDirection, this.chart.duration);
}
}
};
/**
* Adds a area path to equate the start and end paths.
*
* @param {PathOption} options - The options for the path.
* @param {Series} series - The series to which the path belongs.
* @param {string} clipRect - The clip rectangle for the path.
* @returns {void}
* @private
*/
LineBase.prototype.addAreaPath = function (options, series, clipRect) {
var points = this.appendPathElement(options, series, clipRect);
if (points.previousDirection !== '' && options.d !== '') {
var startPathCommands = points.previousDirection.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/g);
var endPathCommands = (options.d).match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/g);
var maxLength = Math.max(startPathCommands.length, endPathCommands.length);
var minLength = Math.min(startPathCommands.length, endPathCommands.length);
if (minLength < endPathCommands.length) {
for (var i = minLength; i < maxLength; i++) {
if (endPathCommands.length !== startPathCommands.length) {
if (endPathCommands.length !== startPathCommands.length) {
if (startPathCommands.length === 1) {
startPathCommands.push(startPathCommands[startPathCommands.length - (options.id.indexOf('border') !== -1 ? 1 : 2)].replace('M', 'L'));
}
else {
startPathCommands.splice(startPathCommands.length - 1, 0, startPathCommands[startPathCommands.length - (options.id.indexOf('border') !== -1 ? 1 : 2)]);
}
}
}
}
animateAddPoints(points.element, options.d, series.chart.redraw, startPathCommands.join(' '), this.chart.duration);
}
else if (startPathCommands.length > endPathCommands.length) {
for (var i = minLength; i < maxLength; i++) {
if (endPathCommands.length !== startPathCommands.length) {
if (series.removedPointIndex === series.points.length) {
if (endPathCommands.length === 1) {
endPathCommands.push(endPathCommands[endPathCommands.length - (options.id.indexOf('border') !== -1 ? 1 : 2)].replace('M', 'L'));
}
else {
endPathCommands.splice(endPathCommands.length - 1, 0, endPathCommands[endPathCommands.length - (options.id.indexOf('border') !== -1 ? 1 : 2)]);
}
}
else {
endPathCommands.splice(1, 0, endPathCommands[1] ? endPathCommands[1] : endPathCommands[0]);
}
}
}
animateAddPoints(points.element, endPathCommands.join(''), series.chart.redraw, points.previousDirection, this.chart.duration, options.d);
}
else {
animateAddPoints(points.element, options.d, series.chart.redraw, points.previousDirection, this.chart.duration);
}
}
};
/**
* To render the marker for the series.
*
* @param {Series} series - The series for which markers are rendered.
* @returns {void}
* @private
*/
LineBase.prototype.renderMarker = function (series) {
if (series.marker && series.marker.visible) {
series.chart.markerRender.render(series);
}
};
/**
* Executes progressive animation for the series.
*
* @param {Series} series - The series for which progressive animation is executed.
* @param {AnimationModel} option - The animation option.
* @returns {void}
* @private
*/
LineBase.prototype.doProgressiveAnimation = function (series, option) {
var animation = new Animation({});
var path = series.pathElement;
var strokeDashArray = path.getAttribute('stroke-dasharray');
var pathLength = series.pathElement.getTotalLength();
var currentTime;
path.style.visibility = 'hidden';
animation.animate(path, {
duration: (option.duration === 0 && animationMode === 'Enable') ? 1000 : option.duration,
delay: option.delay,
progress: function (args) {
path.style.visibility = 'visible';
currentTime = Math.abs(Math.round(((args.timeStamp) * pathLength) / args.duration));
path.setAttribute('stroke-dasharray', currentTime + ',' + pathLength);
},
end: function () {
var annotations = getElement(series.chart.element.id + '_Annotation_Collections');
if (annotations) {
annotations.style.visibility = 'visible';
}
if (series.lastValueLabelElement) {
series.lastValueLabelElement.setAttribute('visibility', 'visible');
}
path.setAttribute('stroke-dasharray', strokeDashArray);
path.style.visibility = '';
series.chart.trigger('animationComplete', { series: series.chart.isBlazor ? {} : series });
}
});
};
/**
* To store the symbol location and region.
*
* @param {Points} point point
* @param {Series} series series
* @param {boolean} isInverted isInverted
* @param {Function} getLocation getLocation
* @returns {void}
* @private
*/
LineBase.prototype.storePointLocation = function (point, series, isInverted, getLocation) {
var markerWidth = (series.marker && series.marker.width) ? series.marker.width : 0;
var markerHeight = (series.marker && series.marker.height) ? series.marker.height : 0;
point.symbolLocations.push(getLocation(point.xValue, point.yValue, series.xAxis, series.yAxis, isInverted, series));
point.regions.push(new Rect(point.symbolLocations[0].x - markerWidth, point.symbolLocations[0].y - markerHeight, 2 * markerWidth, 2 * markerHeight));
};
/**
* Checks if the y-value of a point falls within the y-axis range.
*
* @param {Points} point - The point to be checked.
* @param {Axis} yAxis - The y-axis.
* @returns {boolean} - Returns true if the y-value falls within the y-axis range, otherwise false.
* @private
*/
LineBase.prototype.withinYRange = function (point, yAxis) {
return point.yValue >= yAxis.visibleRange.min && point.yValue <= yAxis.visibleRange.max;
};
LineBase.prototype.GetStepLineDirection = function (currentPoint, previousPoint, stepLineType, command, series, isBorder) {
if (command === void 0) { command = 'L'; }
var X = (series.noRisers && isBorder) ? ' M ' : ' L ';
if (stepLineType === 'Right') {
command = (series.noRisers && isBorder) ? 'M' : 'L';
return (command + ' ' +
(previousPoint.x) + ' ' + (currentPoint.y) + ' L ' + (currentPoint.x) + ' ' + (currentPoint.y) + ' ');
}
else if (stepLineType === 'Center') {
var centerX = previousPoint.x + (currentPoint.x - previousPoint.x) / 2;
return (command + ' ' +
(centerX) + ' ' + (previousPoint.y) + X + (centerX) + ' ' + (currentPoint.y) + ' L ' + (currentPoint.x) + ' ' + (currentPoint.y) + ' ');
}
else {
return (command + ' ' +
(currentPoint.x) + ' ' + (previousPoint.y) + X + (currentPoint.x) + ' ' + (currentPoint.y) + ' ');
}
};
/**
* Gets the first and last visible points from a collection of points.
*
* @param {Points[]} points - Collection of points.
* @returns {{ first: Points, last: Points }} - Returns an object containing the first and last visible points.
* @private
*/
LineBase.prototype.getFirstLastVisiblePoint = function (points) {
var first = null;
var last = null;
for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
var point = points_1[_i];
if (first === null && point.visible) {
first = last = point;
}
last = point.visible ? point : last;
}
return { first: first ? first : points[0], last: last ? last : points[points.length - 1] };
};
/**
* Gets the border direction based on the provided direction.
*
* @param {string} direction - The direction string.
* @returns {string} - Returns the border direction.
* @private
*/
LineBase.prototype.getBorderDirection = function (direction) {
var coordinates = direction.split(' ');
if (coordinates.length > 3 && !(this.chart.stackingAreaSeriesModule) && !(this.chart.stackingStepAreaSeriesModule)) {
coordinates.splice(coordinates.length - 4, 3);
}
else if (this.chart.stackingAreaSeriesModule || this.chart.stackingStepAreaSeriesModule) {
coordinates.splice(coordinates.length / 2 + 1, coordinates.length / 2 + 1);
if (coordinates[coordinates.length - 1] === 'L' || coordinates[coordinates.length - 1] === 'M') {
coordinates.splice(coordinates.length - 1, 1);
}
}
return coordinates.join(' ');
};
/**
* Removes the border from the empty points based on the provided border direction.
*
* @param {string} borderDirection - The border direction.
* @returns {string} - Returns the updated border direction.
* @private
*/
LineBase.prototype.removeEmptyPointsBorder = function (borderDirection) {
var startIndex = 0;
var coordinates = borderDirection.split(' ');
var point;
if (coordinates.length === 4) {
return coordinates.join(' ');
}
do {
point = coordinates.indexOf('M', startIndex);
if (point > -1) {
coordinates.splice(point + 1, 3);
startIndex = point + 1;
if (point - 6 > 0) {
coordinates.splice(point - 6, 6);
startIndex -= 6;
}
}
} while (point !== -1);
return coordinates.join(' ');
};
/**
* Performs linear animation for the series based on the provided animation model.
*
* @param {Series} series - The series to animate.
* @param {AnimationModel} animation - The animation model containing animation details.
* @returns {void}
* @private
*/
LineBase.prototype.doLinearAnimation = function (series, animation) {
var clipRect = series.clipRectElement.childNodes[0].childNodes[0];
var duration = series.chart.animated ? series.chart.duration : animation.duration;
var effect = getAnimationFunction('Linear');
var elementHeight = +clipRect.getAttribute('height');
var elementWidth = +clipRect.getAttribute('width');
var xCenter = +clipRect.getAttribute('x');
var yCenter = series.chart.requireInvertedAxis ? +clipRect.getAttribute('height') + +clipRect.getAttribute('y') :
+clipRect.getAttribute('y');
var value;
clipRect.style.visibility = 'hidden';
this.animateRect(series, animation, clipRect, duration, effect, elementHeight, elementWidth, xCenter, yCenter, value);
if (series.marker && series.marker.visible && series.symbolElement) {
var markerClipRect = series.symbolElement.childNodes[0].childNodes[0];
markerClipRect.style.visibility = 'hidden';
this.animateRect(series, animation, markerClipRect, duration, effect, elementHeight, elementWidth, xCenter, yCenter, value);
}
};
/**
* Animates the given clip rectangle with the specified animation parameters.
*
* @param {Series} series - The series to which the clip rectangle belongs.
* @param {AnimationModel} animation - The animation model containing animation details.
* @param {HTMLElement} clipRect - The clip rectangle to animate.
* @param {number} duration - The duration of the animation.
* @param {Function} effect - The animation function to use.
* @param {number} elementHeight - The height of the clip rectangle element.
* @param {number} elementWidth - The width of the clip rectangle element.
* @param {number} xCenter - The x-coordinate of the clip rectangle's center.
* @param {number} yCenter - The y-coordinate of the clip rectangle's center.
* @param {number} value - The animation value.
* @returns {void}
*/
LineBase.prototype.animateRect = function (series, animation, clipRect, duration, effect, elementHeight, elementWidth, xCenter, yCenter, value) {
new Animation({}).animate(clipRect, {
duration: (duration === 0 && animationMode === 'Enable') ? 1000 : duration,
delay: animation.delay,
progress: function (args) {
clipRect.style.visibility = 'visible';
if (series.chart.requireInvertedAxis) {
value = effect(args.timeStamp, 0, elementHeight, args.duration);
clipRect.setAttribute('transform', 'translate(' + xCenter + ' ' + yCenter +
') scale(1,' + (value / elementHeight) + ') translate(' + (-xCenter) + ' ' + (-yCenter) + ')');
}
else {
value = effect(args.timeStamp, 0, elementWidth, args.duration);
clipRect.setAttribute('transform', 'translate(' + xCenter + ' ' + yCenter +
') scale(' + (value / elementWidth) + ', 1) translate(' + (-xCenter) + ' ' + (-yCenter) + ')');
}
},
end: function () {
var annotations = getElement(series.chart.element.id + '_Annotation_Collections');
if (annotations) {
annotations.style.visibility = 'visible';
}
if (series.lastValueLabelElement) {
series.lastValueLabelElement.setAttribute('visibility', 'visible');
}
var stackLabelGroup = document.getElementById(series.chart.element.id + '_StackLabelGroup');
if (stackLabelGroup) {
markerAnimate(stackLabelGroup, 0, duration, series, null, null, false);
stackLabelGroup.setAttribute('visibility', 'visible');
}
clipRect.setAttribute('transform', 'translate(0,0)');
series.chart.trigger('animationComplete', { series: series.chart.isBlazor ? {} : series });
}
});
};
return LineBase;
}());
export { LineBase };