UNPKG

@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.

257 lines (256 loc) 14 kB
/** * Defines the behavior of a funnel series */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import { PathOption, Rect } from '@syncfusion/ej2-svg-base'; import { appendChildElement, colorNameToHex, convertHexToColor, getElement, removeElement } from '../../common/utils/helper'; import { TriangularBase } from './triangular-base'; /** * The `FunnelSeries` module is used to render the `Funnel` Series. */ var FunnelSeries = /** @class */ (function (_super) { __extends(FunnelSeries, _super); function FunnelSeries() { return _super !== null && _super.apply(this, arguments) || this; } /** * Defines the path of a funnel segment * * @private * @param {AccPoints} point - The point data. * @param {AccumulationSeries} series - The series for which the segment is rendered. * @param {AccumulationChart} chart - The accumulation chart control. * @returns {string} - Get segment data. */ FunnelSeries.prototype.getSegmentData = function (point, series, chart) { var lineWidth; var topRadius; var bottomRadius; var endTop; var endBottom; var minRadius; var endMin; var bottomY; var area = series.triangleSize; var offset = 0; var extraSpace = (chart.initialClipRect.width - series.triangleSize.width) / 2; var emptySpaceAtLeft = extraSpace + chart.initialClipRect.x; var seriesTop = chart.initialClipRect.y + (chart.initialClipRect.height - area.height) / 2; //defines the top and bottom of a segment var top = point.yRatio * area.height; var bottom = top + point.heightRatio * area.height; var neckSize = series.neckSize; lineWidth = neckSize.width + (area.width - neckSize.width) * ((area.height - neckSize.height - top) / (area.height - neckSize.height)); topRadius = (area.width / 2) - lineWidth / 2; //Calculating the middle slope change and bottom endTop = topRadius + lineWidth; if (bottom > area.height - neckSize.height || area.height === neckSize.height) { lineWidth = neckSize.width; } else { lineWidth = neckSize.width + (area.width - neckSize.width) * ((area.height - neckSize.height - bottom) / (area.height - neckSize.height)); } bottomRadius = (area.width / 2) - (lineWidth / 2); endBottom = bottomRadius + lineWidth; if (top >= area.height - neckSize.height) { topRadius = bottomRadius = minRadius = (area.width / 2) - neckSize.width / 2; endTop = endBottom = endMin = (area.width / 2) + neckSize.width / 2; } else if (bottom > (area.height - neckSize.height)) { minRadius = bottomRadius = (area.width / 2) - lineWidth / 2; endMin = endBottom = minRadius + lineWidth; bottomY = area.height - neckSize.height; } top += seriesTop; bottom += seriesTop; bottomY += seriesTop; var line1 = { x: emptySpaceAtLeft + offset + topRadius, y: top }; var line2 = { x: emptySpaceAtLeft + offset + endTop, y: top }; var line4 = { x: emptySpaceAtLeft + offset + endBottom, y: bottom }; var line5 = { x: emptySpaceAtLeft + offset + bottomRadius, y: bottom }; var line3 = { x: emptySpaceAtLeft + offset + endBottom, y: bottom }; var line6 = { x: emptySpaceAtLeft + offset + bottomRadius, y: bottom }; if (bottomY) { line3 = { x: emptySpaceAtLeft + offset + endMin, y: bottomY }; line6 = { x: emptySpaceAtLeft + offset + minRadius, y: bottomY }; } var polygon = [line1, line2, line3, line4, line5, line6]; this.setLabelLocation(series, point, polygon); var direction = this.findPath(polygon, point, series); return direction; }; /** * Renders a funnel segment. * * @private * @param {AccPoints} point - The point data. * @param {AccumulationSeries} series - The series for which the segment is rendered. * @param {AccumulationChart} chart - The accumulation chart control. * @param {PathOption} options - The rendering options for the segment. * @param {Element} seriesGroup - The group element to contain the funnel segments. * @param {boolean} redraw - Specifies whether to redraw the segment. * @param {string} previousRadius - Specifies the previous radius of the pie when animating the individual series point. * @param {Object[]} previousCenter - Specifies the previous center of the pie when animating the individual series point. * @param {boolean} pointAnimation - Specifies whether the point based animation is enabled. * @returns {void} */ FunnelSeries.prototype.renderPoint = function (point, series, chart, options, seriesGroup, redraw, previousRadius, previousCenter, pointAnimation) { if (!point.visible) { removeElement(options.id); return null; } var previousDirection; var direction = this.getSegmentData(point, series, chart); point.midAngle = 0; options.d = direction; if (pointAnimation && document.getElementById(options.id)) { previousDirection = document.getElementById(options.id).getAttribute('d'); } var element = chart.renderer.drawPath(options); element.setAttribute('role', series.accessibility.accessibilityRole ? series.accessibility.accessibilityRole : 'img'); element.setAttribute('tabindex', (point.index === 0 && series.accessibility.focusable) ? String(series.accessibility.tabIndex) : '-1'); element.setAttribute('aria-label', series.accessibility.accessibilityDescription ? series.accessibility.accessibilityDescription : (point.x + ':' + point.y + '%. ' + series.name)); appendChildElement(false, seriesGroup, element, redraw, pointAnimation ? pointAnimation : undefined, pointAnimation ? 'x' : undefined, pointAnimation ? 'y' : undefined, undefined, pointAnimation ? previousDirection : undefined, undefined, undefined, undefined, pointAnimation ? chart.duration : undefined); if (point.isExplode) { chart.accBaseModule.explodePoints(point.index, chart, true); } }; /** * Renders the Trapezoidal funnel series in an accumulation chart. * * @param {AccumulationSeries} series - The series data for the Trapezoidal funnel. * @param {AccPoints[]} points - The data points for the series. * @param {AccumulationChart} chart - The instance of the accumulation chart. * @param {PathOption[]} options - The path options for rendering the Trapezoidal funnel. * @param {Element} seriesGroup - The group element for the series. * @param {boolean} redraw - Specifies whether to redraw the series. * @returns {void} - This method does not return a value. */ FunnelSeries.prototype.renderTrapezoidalFunnel = function (series, points, chart, options, seriesGroup, redraw) { var funnelWidth = series.triangleSize.width; var funnelHeight = series.triangleSize.height; var horizontalMargin = (chart.initialClipRect.width - funnelWidth) / 2; var leftMargin = horizontalMargin + chart.initialClipRect.x; var funnelTop = chart.initialClipRect.y + (chart.initialClipRect.height - funnelHeight) / 2; var maxPointValue = Math.max.apply(Math, points.map(function (d) { return d.y; })); var barPadding = 10; var currentVerticalOffset = 0; var polygonGroup = redraw ? getElement(chart.element.id + '_Series_' + series.index + '_Polygon') : chart.renderer.createGroup({ id: chart.element.id + '_Series_' + series.index + '_Polygon' }); for (var i = 0; i < series.points.length; i++) { var point = series.points[i]; var pathOption = options[point.index]; if (!point.visible) { removeElement(pathOption.id); removeElement(pathOption.id + '_polygon'); continue; } var availableHeight = funnelHeight - barPadding * (points.length - 1); var barHeight = availableHeight / points.length; var barWidth = funnelWidth * (point.y / maxPointValue); var visiblePointIndex = points.indexOf(point); var nextBarWidth = visiblePointIndex < points.length - 1 ? funnelWidth * (points[visiblePointIndex + 1].y / maxPointValue) : 0; var x = leftMargin + (funnelWidth - barWidth) / 2; var y = funnelTop + currentVerticalOffset; var cornerRadius = Math.min(series.borderRadius, barHeight / 2); var rectPath = 'M' + (x + cornerRadius) + ' ' + y + ' ' + 'L' + (x + barWidth - cornerRadius) + ' ' + y + ' ' + 'A' + cornerRadius + ' ' + cornerRadius + ' 0 0 1 ' + (x + barWidth) + ' ' + (y + cornerRadius) + ' ' + 'L' + (x + barWidth) + ' ' + (y + barHeight - cornerRadius) + ' ' + 'A' + cornerRadius + ' ' + cornerRadius + ' 0 0 1 ' + (x + barWidth - cornerRadius) + ' ' + (y + barHeight) + ' ' + 'L' + (x + cornerRadius) + ' ' + (y + barHeight) + ' ' + 'A' + cornerRadius + ' ' + cornerRadius + ' 0 0 1 ' + x + ' ' + (y + barHeight - cornerRadius) + ' ' + 'L' + x + ' ' + (y + cornerRadius) + ' ' + 'A' + cornerRadius + ' ' + cornerRadius + ' 0 0 1 ' + (x + cornerRadius) + ' ' + y + ' ' + 'Z'; point.midAngle = 0; pathOption.d = rectPath; var element = chart.renderer.drawPath(pathOption); element.setAttribute('role', series.accessibility.accessibilityRole ? series.accessibility.accessibilityRole : 'img'); element.setAttribute('tabindex', (point.index === 0 && series.accessibility.focusable) ? String(series.accessibility.tabIndex) : '-1'); element.setAttribute('aria-label', series.accessibility.accessibilityDescription ? series.accessibility.accessibilityDescription : (point.x + ':' + point.y + '%. ' + series.name)); appendChildElement(false, seriesGroup, element, redraw); if (visiblePointIndex < points.length - 1) { var polygonOption = new PathOption(pathOption.id + '_polygon', this.lightenColor(pathOption.fill), pathOption['stroke-width'] * 0.4, this.lightenColor(pathOption.stroke), pathOption.opacity, pathOption['stroke-dasharray'], ''); var trapezoidPoints = [ [(funnelWidth - barWidth) / 2 + leftMargin + cornerRadius, y + barHeight], [(funnelWidth + barWidth) / 2 + leftMargin - cornerRadius, y + barHeight], [(funnelWidth + nextBarWidth) / 2 + leftMargin, y + barHeight + barPadding], [(funnelWidth - nextBarWidth) / 2 + leftMargin, y + barHeight + barPadding] ]; var trapezoidPath = '' + 'M' + trapezoidPoints[0][0] + ' ' + trapezoidPoints[0][1] + ' ' + 'L' + trapezoidPoints[1][0] + ' ' + trapezoidPoints[1][1] + ' ' + 'L' + trapezoidPoints[2][0] + ' ' + trapezoidPoints[2][1] + ' ' + 'L' + trapezoidPoints[3][0] + ' ' + trapezoidPoints[3][1] + ' ' + 'Z'; polygonOption.d = trapezoidPath; var polygon = chart.renderer.drawPath(polygonOption); appendChildElement(false, polygonGroup, polygon, redraw); } else { removeElement(pathOption.id + '_polygon'); } currentVerticalOffset += barHeight + barPadding; point.region = new Rect(x, y, barWidth, barHeight); point.symbolLocation = { x: point.region.x + point.region.width / 2, y: point.region.y + point.region.height / 2 }; point.labelOffset = { x: point.symbolLocation.x + point.region.width / 2, y: point.symbolLocation.y + point.region.height / 2 }; if (point.isExplode) { chart.accBaseModule.explodePoints(point.index, chart, true); } } appendChildElement(false, chart.getSeriesElement(), seriesGroup, redraw); appendChildElement(false, chart.getSeriesElement(), polygonGroup, redraw); }; /** * Function to lighten a color by blending it with white. * * @param {string} color - The main color in hex format (e.g., '#1e90ff'). * @returns {string} - The lightened color in hex format. */ FunnelSeries.prototype.lightenColor = function (color) { var rgbValue = convertHexToColor(colorNameToHex(color)); return 'rgb(' + rgbValue.r + ',' + rgbValue.g + ',' + rgbValue.b + ',' + 0.4 + ')'; }; /** * To get the module name of the funnel series. * * @returns {string} - Get module name. */ FunnelSeries.prototype.getModuleName = function () { return 'FunnelSeries'; }; /** * To destroy the funnel series. * * @returns {void} Destroy method. * @private */ FunnelSeries.prototype.destroy = function () { /** * Destroys the funnel series. */ }; return FunnelSeries; }(TriangularBase)); export { FunnelSeries };