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.

988 lines 125 kB
import { createElement, isNullOrUndefined, Animation } from '@syncfusion/ej2-base'; import { DataUtil } from '@syncfusion/ej2-data'; import { subtractThickness, valueToCoefficient, sum, redrawElement, isBreakLabel, ChartLocation, withInBounds, rotateTextSize, removeElement, calculateScrollbarOffset } from '../../common/utils/helper'; import { subArray, inside, appendChildElement, stringToNumber } from '../../common/utils/helper'; import { Thickness, logBase, createZoomingLabels, getElement } from '../../common/utils/helper'; import { Size, Rect, measureText, TextOption, PathOption } from '@syncfusion/ej2-svg-base'; import { textElement, textTrim, getRotatedRectangleCoordinates, isRotatedRectIntersect, isZoomSet } from '../../common/utils/helper'; /** * Specifies the Cartesian Axis Layout. */ var axisPadding = 10; var CartesianAxisLayoutPanel = /** @class */ (function () { /** @private */ /** * Constructor for creating the chart. * * @param {Chart} chartModule - Specifies the Chart model. * @private */ function CartesianAxisLayoutPanel(chartModule) { this.chart = chartModule; this.padding = 5; } /** * Measure the axis size. * * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.measureAxis = function (rect) { var chart = this.chart; var chartAreaWidth = chart.chartArea.width ? stringToNumber(chart.chartArea.width, chart.availableSize.width) : null; this.crossAt(chart); this.seriesClipRect = new Rect(rect.x, rect.y, rect.width, rect.height); this.initialClipRect = rect; this.leftSize = 0; this.rightSize = 0; this.topSize = 0; this.bottomSize = 0; //Measure Axis size with initial Rect this.measureRowAxis(chart, this.initialClipRect); this.initialClipRect = subtractThickness(this.initialClipRect, new Thickness(this.leftSize, this.rightSize, 0, 0)); this.measureColumnAxis(chart, this.initialClipRect); this.initialClipRect = subtractThickness(this.initialClipRect, new Thickness(0, 0, this.topSize, this.bottomSize)); if (!this.chart.delayRedraw) { this.calculateAxisSize(this.initialClipRect); } this.leftSize = 0; this.rightSize = 0; this.topSize = 0; this.bottomSize = 0; //Measure Axis size with series Rect this.measureRowAxis(chart, this.initialClipRect); this.seriesClipRect = subtractThickness(this.seriesClipRect, new Thickness(this.leftSize, this.rightSize, 0, 0)); this.measureColumnAxis(chart, this.initialClipRect); this.seriesClipRect = subtractThickness(this.seriesClipRect, new Thickness(0, 0, this.topSize, this.bottomSize)); if (chartAreaWidth) { this.calculateFixedChartArea(chart, chartAreaWidth); } if (!this.chart.delayRedraw) { chart.refreshAxis(); this.calculateAxisSize(this.seriesClipRect); } }; CartesianAxisLayoutPanel.prototype.calculateFixedChartArea = function (chart, chartAreaWidth) { this.seriesClipRect.width = chartAreaWidth; this.seriesClipRect.x = chart.availableSize.width - chart.margin.right - chartAreaWidth - (chart.legendSettings.position === 'Right' ? chart.legendModule.legendBounds.width : 0); for (var _i = 0, _a = chart.rows; _i < _a.length; _i++) { var item = _a[_i]; this.seriesClipRect.x -= sum(item.farSizes); } }; CartesianAxisLayoutPanel.prototype.measureRowAxis = function (chart, rect) { var row; this.calculateRowSize(rect); for (var _i = 0, _a = chart.rows; _i < _a.length; _i++) { var item = _a[_i]; row = item; row.nearSizes = []; row.farSizes = []; row.insideNearSizes = []; row.insideFarSizes = []; this.arrangeAxis(row); this.measureDefinition(row, chart, new Size(chart.availableSize.width, row.computedHeight)); if (this.leftSize < sum(row.nearSizes)) { this.leftSize = sum(row.nearSizes); } if (this.rightSize < sum(row.farSizes)) { this.rightSize = sum(row.farSizes); } } }; CartesianAxisLayoutPanel.prototype.measureColumnAxis = function (chart, rect) { var column; this.calculateColumnSize(rect); for (var _i = 0, _a = chart.columns; _i < _a.length; _i++) { var item = _a[_i]; column = item; column.farSizes = []; column.nearSizes = []; column.insideNearSizes = []; column.insideFarSizes = []; this.arrangeAxis(column); this.measureDefinition(column, chart, new Size(column.computedWidth, chart.availableSize.height)); if (this.bottomSize < sum(column.nearSizes)) { this.bottomSize = sum(column.nearSizes); } if (this.topSize < sum(column.farSizes)) { this.topSize = sum(column.farSizes); } } }; /** * Measure the column and row in chart. * * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.measureDefinition = function (definition, chart, size) { var ele; for (var _i = 0, _a = definition.axes; _i < _a.length; _i++) { var axis = _a[_i]; ele = axis.scrollbarSettings.height; axis.scrollBarHeight = chart.scrollBarModule && chart.zoomModule && chart.zoomSettings.enableScrollbar && axis.enableScrollbarOnZooming && chart.zoomModule.isZoomed && (axis.zoomFactor < 1 || axis.zoomPosition > 0) ? ele : 0; axis.scrollBarHeight = chart.scrollBarModule && (chart.zoomModule && chart.zoomSettings.enableScrollbar && axis.enableScrollbarOnZooming && chart.zoomModule.isZoomed && (axis.zoomFactor < 1 || axis.zoomPosition > 0) || axis.scrollbarSettings.enable) ? ele : 0; axis.getModule(chart); axis.baseModule.calculateRangeAndInterval(size, axis); definition.computeSize(axis, axis.scrollBarHeight, definition, chart); } if (definition.farSizes.length > 0) { definition.farSizes[definition.farSizes.length - 1] -= axisPadding; } if (definition.nearSizes.length > 0) { definition.nearSizes[definition.nearSizes.length - 1] -= axisPadding; } }; /** * Measure the axis. * * @param {Rect} rect - The rect for measuring the axis. * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.calculateAxisSize = function (rect) { var chart = this.chart; var row; var column; var definition; var axis; var nearCount = 0; var farCount = 0; var size = 0; var x; var y; var axisOffset; this.calculateRowSize(rect); var isPrimaryYaxisOutside = false; for (var i = 0, len = chart.rows.length; i < len; i++) { row = chart.rows[i]; nearCount = 0; farCount = 0; for (var j = 0, len_1 = row.axes.length; j < len_1; j++) { axis = row.axes[j]; axisOffset = axis.plotOffset; if (axis.rect.height === 0) { axis.rect.height = row.computedHeight; size = 0; for (var k = i + 1, len_2 = i + axis.span; k < len_2; k++) { definition = chart.rows[k]; size += definition.computedHeight; } axis.rect.y = (row.computedTop - size) + (axis.plotOffsetTop ? axis.plotOffsetTop : axisOffset); axis.rect.height = (axis.rect.height + size) - (this.getAxisOffsetValue(axis.plotOffsetTop, axis.plotOffsetBottom, axis.plotOffset)); axis.rect.width = 0; } if (axis.name === 'primaryYAxis' && axis.labelPosition === 'Outside') { isPrimaryYaxisOutside = true; } if (axis.isAxisOpposedPosition) { if (axis.labelPosition === 'Inside' && axis.orientation === 'Vertical') { if (farCount > 0) { x = rect.x + rect.width + sum(subArray(row.farSizes, farCount)) + axis.maxLabelSize.width + axis.multiLevelLabelHeight + (axis.tickPosition === 'Inside' ? axis.majorTickLines.height : 0) + axis.labelPadding; } else { x = rect.x + rect.width - sum(subArray(row.insideFarSizes, farCount)); } } else { x = rect.x + rect.width + sum(subArray(row.farSizes, farCount)); } axis.rect.x = axis.rect.x >= x ? axis.rect.x : x; farCount++; } else { if (axis.labelPosition === 'Inside' && axis.orientation === 'Vertical') { x = rect.x + sum(subArray(row.insideNearSizes, isPrimaryYaxisOutside ? nearCount - 1 : nearCount)); } else { x = rect.x - sum(subArray(row.nearSizes, nearCount)); } axis.rect.x = axis.rect.x <= x ? axis.rect.x : x; nearCount++; } } } this.calculateColumnSize(rect); for (var i = 0, len = chart.columns.length; i < len; i++) { column = chart.columns[i]; nearCount = 0; farCount = 0; for (var j = 0, len_3 = column.axes.length; j < len_3; j++) { axis = column.axes[j]; axisOffset = axis.plotOffset; if (axis.rect.width === 0) { for (var k = i, len_4 = (i + axis.span); k < len_4; k++) { definition = chart.columns[k]; axis.rect.width += definition.computedWidth; } axis.rect.x = column.computedLeft + (axis.plotOffsetLeft ? axis.plotOffsetLeft : axisOffset); axis.rect.width -= (this.getAxisOffsetValue(axis.plotOffsetLeft, axis.plotOffsetRight, axis.plotOffset)); axis.rect.height = 0; } if (axis.isAxisOpposedPosition) { if (axis.labelPosition === 'Inside' && axis.orientation === 'Horizontal') { if (farCount > 0) { y = rect.y - sum(subArray(column.farSizes, farCount)) - axis.maxLabelSize.height - axis.multiLevelLabelHeight - (axis.tickPosition === 'Inside' ? axis.majorTickLines.height : 0) - axis.labelPadding; } else { y = rect.y + sum(subArray(column.insideFarSizes, farCount)); } } else { y = rect.y - sum(subArray(column.farSizes, farCount)); } axis.rect.y = axis.rect.y <= y ? axis.rect.y : y; farCount++; } else { if (axis.labelPosition === 'Inside' && axis.orientation === 'Horizontal') { if (nearCount > 0) { y = rect.y + rect.height + sum(subArray(column.nearSizes, nearCount)) + axis.maxLabelSize.height + axis.multiLevelLabelHeight + (axis.tickPosition === 'Inside' ? axis.majorTickLines.height : 0) + axis.labelPadding; } else { y = rect.y + rect.height - sum(subArray(column.insideNearSizes, nearCount)); } } else { y = rect.y + rect.height + sum(subArray(column.nearSizes, nearCount)); } axis.rect.y = axis.rect.y >= y ? axis.rect.y : y; nearCount++; } } } }; /** * Measure the axis. * * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.measure = function () { var chart = this.chart; var row; var column; var definition; var actualIndex; var span; for (var _i = 0, _a = chart.axisCollections; _i < _a.length; _i++) { var axis = _a[_i]; //definition.Axes = axis; if (axis.orientation === 'Vertical') { chart.verticalAxes.push(axis); actualIndex = this.getActualRow(axis); row = chart.rows[actualIndex]; this.pushAxis(row, axis); span = ((actualIndex + axis.span) > chart.rows.length ? chart.rows.length : (actualIndex + axis.span)); for (var j = actualIndex + 1; j < span; j++) { definition = chart.rows[j]; definition.axes[row.axes.length - 1] = axis; chart.rows[j] = definition; } chart.rows[actualIndex] = row; } else { chart.horizontalAxes.push(axis); actualIndex = this.getActualColumn(axis); column = chart.columns[actualIndex]; this.pushAxis(column, axis); span = ((actualIndex + axis.span) > chart.columns.length ? chart.columns.length : (actualIndex + axis.span)); for (var j = actualIndex + 1; j < span; j++) { definition = chart.columns[j]; definition.axes[column.axes.length - 1] = axis; chart.columns[j] = definition; } chart.columns[actualIndex] = column; } axis.isRTLEnabled = chart.enableRtl; axis.setIsInversedAndOpposedPosition(); } }; CartesianAxisLayoutPanel.prototype.getAxisOffsetValue = function (position1, position2, plotOffset) { var rangeOffset = position1 ? (position1 + (position2 ? position2 : plotOffset)) : (position2 ? position2 + plotOffset : 2 * plotOffset); return rangeOffset; }; CartesianAxisLayoutPanel.prototype.crossAt = function (chart) { for (var _i = 0, _a = chart.axisCollections; _i < _a.length; _i++) { var axis = _a[_i]; if (axis.crossesAt === null) { continue; } if (!axis.crossesInAxis) { if (chart.requireInvertedAxis) { axis.crossInAxis = ((axis.orientation === 'Horizontal')) ? chart.primaryXAxis : chart.primaryYAxis; } else { axis.crossInAxis = ((axis.orientation === 'Horizontal')) ? chart.primaryYAxis : chart.primaryXAxis; } axis.crossAt = this.updateCrossAt(axis.crossInAxis, axis.crossesAt); continue; } else { for (var i = 2, len = chart.axisCollections.length; i < len; i++) { if (axis.crossesInAxis === chart.axisCollections[i].name) { axis.crossInAxis = chart.axisCollections[i]; axis.crossAt = this.updateCrossAt(axis.crossInAxis, axis.crossesAt); continue; } } } } }; CartesianAxisLayoutPanel.prototype.updateCrossAt = function (axis, crossAt) { switch (axis.valueType) { case 'DateTime': { var option = { skeleton: 'full', type: 'dateTime' }; var dateParser = this.chart.intl.getDateParser(option); var dateFormatter = this.chart.intl.getDateFormat(option); return Date.parse(dateParser(dateFormatter(new Date(DataUtil.parse.parseJson({ val: crossAt }).val)))); } case 'Category': return parseFloat(crossAt) ? parseFloat(crossAt) : axis.labels.indexOf(crossAt); case 'Logarithmic': return logBase(crossAt, axis.logBase); default: return crossAt; } }; CartesianAxisLayoutPanel.prototype.pushAxis = function (definition, axis) { for (var i = 0, len = definition.axes.length; i <= len; i++) { if (!definition.axes[i]) { definition.axes[i] = axis; break; } } }; CartesianAxisLayoutPanel.prototype.arrangeAxis = function (definition) { var axisCollection = []; for (var i = 0, len = definition.axes.length; i <= len; i++) { if (definition.axes[i]) { axisCollection.push(definition.axes[i]); } } definition.axes = axisCollection; }; CartesianAxisLayoutPanel.prototype.getActualColumn = function (axis) { var actualLength = this.chart.columns.length; var pos = axis.columnIndex; var result = pos >= actualLength ? actualLength - 1 : (pos < 0 ? 0 : pos); return result; }; CartesianAxisLayoutPanel.prototype.getActualRow = function (axis) { var actualLength = this.chart.rows.length; var pos = axis.rowIndex; var result = pos >= actualLength ? actualLength - 1 : (pos < 0 ? 0 : pos); return result; }; /** * Measure the row size. * * @returns {void} */ CartesianAxisLayoutPanel.prototype.calculateRowSize = function (rect) { /** Calculate row size */ var chart = this.chart; var row; var rowTop = rect.y + rect.height; var height = 0; var remainingHeight = Math.max(0, rect.height); for (var i = 0, len = chart.rows.length; i < len; i++) { row = chart.rows[i]; if (row.height.indexOf('%') !== -1) { height = Math.min(remainingHeight, (rect.height * parseInt(row.height, 10) / 100)); } else { height = Math.min(remainingHeight, parseInt(row.height, 10)); } height = (i !== (len - 1)) ? height : remainingHeight; row.computedHeight = height; rowTop -= height; row.computedTop = rowTop; remainingHeight -= height; } }; /** * Measure the row size. * * @param {Rect} rect rect * @returns {void} */ CartesianAxisLayoutPanel.prototype.calculateColumnSize = function (rect) { /** Calculate column size */ var chart = this.chart; var column; var columnLeft = rect.x; var width = 0; var remainingWidth = Math.max(0, rect.width); for (var i = 0, len = chart.columns.length; i < len; i++) { column = chart.columns[i]; if (column.width.indexOf('%') !== -1) { width = Math.min(remainingWidth, (rect.width * parseInt(column.width, 10) / 100)); } else { width = Math.min(remainingWidth, parseInt(column.width, 10)); } width = (i !== (len - 1)) ? width : remainingWidth; column.computedWidth = width; column.computedLeft = columnLeft; columnLeft += width; remainingWidth -= width; } }; /** * To render the axis element. * * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.renderAxes = function () { var chart = this.chart; var axis; var axisElement = chart.renderer.createGroup({ id: chart.element.id + 'AxisInsideCollection' }); var axisLineElement = chart.renderer.createGroup({ id: chart.element.id + 'AxisOutsideCollection' }); if (axisLineElement) { axisLineElement.setAttribute('aria-hidden', 'true'); } var outsideElement; var isInside; if (chart.scrollBarModule) { chart.scrollBarModule.topScrollBarCount = 0; chart.scrollBarModule.bottomScrollBarCount = 0; chart.scrollBarModule.leftScrollBarCount = 0; chart.scrollBarModule.rightScrollBarCount = 0; } for (var i = 0, len = chart.axisCollections.length; i < len; i++) { var axisVisibility = true; axis = chart.axisCollections[i]; axis.index = i; this.element = chart.renderer.createGroup({ id: chart.element.id + 'AxisGroup' + i + 'Inside' }); if (this.element) { this.element.setAttribute('aria-hidden', 'true'); } outsideElement = chart.renderer.createGroup({ id: chart.element.id + 'AxisGroup' + i + 'Outside' }); if (outsideElement) { outsideElement.setAttribute('aria-hidden', 'true'); } for (var _i = 0, _a = axis.series; _i < _a.length; _i++) { var series = _a[_i]; if (axis.name === series.yAxisName || axis.name === series.xAxisName) { axisVisibility = series.visible; if (series.category === 'Pareto' && !series.paretoOptions.showAxis && series.type === 'Line') { axisVisibility = false; } if (!axisVisibility) { continue; } else { break; } } } if (!axisVisibility) { if (axis.zoomingScrollBar) { axis.zoomingScrollBar.removeScrollSvg(); } continue; } isInside = this.findAxisPosition(axis); this.drawAxis(axis, i, isInside, outsideElement, axisElement, axisLineElement); } this.drawPaneLines(chart, axisElement); appendChildElement(chart.enableCanvas, chart.svgObject, axisElement, chart.redraw); return axisLineElement; }; /** * To render the axis scrollbar * * @param {Chart} chart chart * @param {Axis} axis axis * @returns {void} */ CartesianAxisLayoutPanel.prototype.renderScrollbar = function (chart, axis) { var isZoomed = isNullOrUndefined(chart.zoomModule) ? false : chart.zoomModule.isZoomed; if (!axis.zoomingScrollBar) { chart.scrollBarModule.injectTo(axis, chart); } if (((isZoomed && (axis.zoomFactor < 1 || axis.zoomPosition > 0)) || (axis.scrollbarSettings.enable && (axis.zoomFactor <= 1 || axis.zoomPosition >= 0))) && (!axis.zoomingScrollBar.isScrollUI)) { if (!chart.scrollElement) { chart.scrollElement = redrawElement(chart.redraw, chart.element.id + '_scrollElement') || createElement('div', { id: chart.element.id + '_scrollElement' }); } appendChildElement(false, chart.scrollElement, axis.zoomingScrollBar.render(true), true); } else if (axis.zoomFactor === 1 && axis.zoomPosition === 0 && axis.zoomingScrollBar.svgObject && !axis.scrollbarSettings.enable) { axis.zoomingScrollBar.destroy(); } else if (axis.zoomingScrollBar.svgObject) { var topOffset = (axis.isAxisOpposedPosition && axis.orientation === 'Horizontal' ? -16 : 0) + axis.rect.y + Math.max(0.5, axis.lineStyle.width / 2); var leftOffset = (axis.isAxisOpposedPosition && axis.orientation !== 'Horizontal' ? 16 : 0) + (axis.rect.x - (axis.orientation === 'Horizontal' && axis.scrollbarSettings.enableZoom ? axis.zoomingScrollBar.svgExtraWidth / 2 : 0)) - (axis.orientation === 'Vertical' ? axis.scrollbarSettings.height : 0); if (axis.orientation !== 'Horizontal' && (axis.scrollbarSettings.position === 'Left' || axis.scrollbarSettings.position === 'Right')) { leftOffset = calculateScrollbarOffset(axis.zoomingScrollBar, false); } else if (axis.orientation === 'Horizontal' && (axis.scrollbarSettings.position === 'Top' || axis.scrollbarSettings.position === 'Bottom')) { topOffset = calculateScrollbarOffset(axis.zoomingScrollBar, true); } axis.zoomingScrollBar.svgObject.style.top = topOffset + 'px'; axis.zoomingScrollBar.svgObject.style.left = leftOffset + 'px'; } if (axis.zoomingScrollBar.isScrollUI) { axis.zoomingScrollBar.isScrollUI = false; } }; /** * Draws pane lines for the specified chart. * * @param {Chart} chart -The chart for which pane lines are to be drawn. * @param {Element} [axisElement] -Optional. The axis element to which the pane lines are associated. * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.drawPaneLines = function (chart, axisElement) { this.element = chart.renderer.createGroup({ id: chart.element.id + 'DefinitionLine' }); for (var j = 0, len = chart.rows.length; j < len; j++) { var row = chart.rows[j]; if (row.border.color) { this.drawBottomLine(row, j, true); } } for (var j = 0, len = chart.columns.length; j < len; j++) { var column = chart.columns[j]; if (column.border.color) { this.drawBottomLine(column, j, false); } } axisElement = axisElement ? axisElement : getElement(chart.element.id + 'AxisInsideCollection'); if (!this.chart.enableCanvas) { axisElement.appendChild(this.element); } }; /** * Draws an axis for the specified axis configuration. * * @private * @param {Axis} axis -The axis configuration to be drawn. * @param {number} index -The index of the axis. * @param {boolean} isInside -Indicates whether the axis is inside or outside the plot area. * @param {Element} outsideElement -The element where the axis should be drawn if it's outside the plot area. * @param {Element} axisElement -The element representing the axis. * @param {Element} axisLineElement -The element representing the axis line. * @returns {void} */ CartesianAxisLayoutPanel.prototype.drawAxis = function (axis, index, isInside, outsideElement, axisElement, axisLineElement) { axis.updateCrossValue(); var axisName = ''; if (axis.orientation === 'Horizontal') { if (axis.visible && axis.internalVisibility && axis.lineStyle.width > 0) { this.drawAxisLine(axis, index, axis.plotOffset, 0, 0, 0, axis.plotOffsetLeft, axis.plotOffsetRight, isInside ? outsideElement : this.element, axis.updatedRect); } axisName = 'X'; } else { if (axis.visible && axis.internalVisibility && axis.lineStyle.width > 0) { this.drawAxisLine(axis, index, 0, axis.plotOffset, axis.plotOffsetBottom, axis.plotOffsetTop, 0, 0, isInside ? outsideElement : this.element, axis.updatedRect); } axisName = 'Y'; } if (axis.majorGridLines.width > 0 || axis.majorTickLines.width > 0 || axis.minorTickLines.width > 0 || axis.minorGridLines.width > 0) { this['draw' + axisName + 'AxisGridLine'](axis, index, (isInside || axis.tickPosition === 'Inside') ? outsideElement : this.element, axis.updatedRect); } if (axis.visible && axis.internalVisibility) { this['draw' + axisName + 'AxisLabels'](axis, index, (isInside || axis.labelPosition === 'Inside') ? outsideElement : this.element, (axis.placeNextToAxisLine ? axis.updatedRect : axis.rect)); this['draw' + axisName + 'AxisBorder'](axis, index, (isInside || axis.labelPosition === 'Inside') ? outsideElement : this.element, (axis.placeNextToAxisLine ? axis.updatedRect : axis.rect)); this['draw' + axisName + 'AxisTitle'](axis, index, isInside ? outsideElement : this.element, (axis.placeNextToAxisLine ? axis.updatedRect : axis.rect)); } if (!this.chart.enableCanvas) { axisElement.appendChild(this.element); if (outsideElement && outsideElement.childNodes.length > 0) { axisLineElement.appendChild(outsideElement); } } if (this.chart.scrollBarModule && ((this.chart.zoomSettings.enableScrollbar && axis.enableScrollbarOnZooming) || axis.scrollbarSettings.enable)) { this.renderScrollbar(this.chart, axis); } else { if (axis.zoomingScrollBar) { axis.zoomingScrollBar.destroy(); } } }; /** * To find the axis position * * @param {Axis} axis axis * @returns {boolean} axis position * @private */ CartesianAxisLayoutPanel.prototype.findAxisPosition = function (axis) { return axis.crossAt !== null && axis.isInside(axis.crossInAxis.visibleRange); }; /** * To render the bootom line of the columns and rows * * @param {Row | Column} definition definition * @param {number} index index * @param {boolean} isRow isRow * @returns {void} */ CartesianAxisLayoutPanel.prototype.drawBottomLine = function (definition, index, isRow) { var chart = this.chart; var optionsLine = {}; var x1; var x2; var y1; var y2; var definitionName; if (isRow) { definition = definition; y1 = y2 = definition.computedTop + definition.computedHeight; x1 = this.seriesClipRect.x; x2 = x1 + this.seriesClipRect.width; definitionName = 'Row'; } else { definition = definition; x1 = x2 = definition.computedLeft; y1 = this.seriesClipRect.y; y2 = y1 + this.seriesClipRect.height; definitionName = 'Column'; } optionsLine = { 'id': chart.element.id + '_AxisBottom_' + definitionName + index, x1: x1, y1: y1, x2: x2, y2: y2, 'stroke-width': definition.border.width, 'stroke': definition.border.color }; this.htmlObject = chart.renderer.drawLine(optionsLine); appendChildElement(chart.enableCanvas, this.element, this.htmlObject); }; /** * To render the axis line * * @param {Axis} axis axis * @param {number} index index * @param {number} plotX plotX * @param {number} plotY plotY * @param {number} plotBottom plotBottom * @param {number} plotTop plotTop * @param {number} plotLeft plotLeft * @param {number} plotRight plotRight * @param {Element} parent parent * @param {Rect} rect rect * @returns {void} */ CartesianAxisLayoutPanel.prototype.drawAxisLine = function (axis, index, plotX, plotY, plotBottom, plotTop, plotLeft, plotRight, parent, rect) { var chart = this.chart; var optionsLine = {}; var element = getElement(chart.element.id + 'AxisLine_' + index); var direction = element ? element.getAttribute('d') : ''; element = null; optionsLine = { 'id': chart.element.id + 'AxisLine_' + index, 'd': 'M ' + (rect.x - plotX - plotLeft) + ' ' + (rect.y - plotY - plotTop) + ' L ' + (rect.x + rect.width + plotX + plotRight) + ' ' + (rect.y + rect.height + plotY + plotBottom), 'stroke-dasharray': axis.lineStyle.dashArray, 'stroke-width': axis.lineStyle.width, 'stroke': axis.lineStyle.color || chart.themeStyle.axisLine }; this.htmlObject = chart.renderer.drawPath(optionsLine); appendChildElement(chart.enableCanvas, parent, this.htmlObject, chart.redraw, true, 'x', 'y', null, direction, null, null, null, chart.duration); }; /** * To render the yAxis grid line * * @param {Axis} axis axis * @param {number} index index * @param {Element} parent parent * @param {Rect} rect rect * @returns {void} */ CartesianAxisLayoutPanel.prototype.drawYAxisGridLine = function (axis, index, parent, rect) { var isLogAxis = axis.valueType === 'Logarithmic'; var isCategoryAxis = axis.valueType.indexOf('Category') > -1; var tempInterval; var pointY = 0; var majorGrid = ''; var majorTick = ''; var minorGridDirection; var isOpposed = axis.isAxisOpposedPosition; var tickSize = isOpposed ? axis.majorTickLines.height : -axis.majorTickLines.height; var axisLineSize = (isOpposed) ? axis.lineStyle.width * 0.5 : -axis.lineStyle.width * 0.5; var ticksbwtLabel = (axis.valueType === 'Category' && axis.labelPlacement === 'BetweenTicks') ? 0.5 : 0; var scrollBarHeight = (isNullOrUndefined(axis.crossesAt) && axis.scrollbarSettings.position !== 'Right' && axis.scrollbarSettings.position !== 'Left') ? isOpposed ? axis.scrollBarHeight : -axis.scrollBarHeight : 0; var isTickInside = axis.tickPosition === 'Inside'; var ticks = isTickInside ? (rect.x - tickSize - axisLineSize) : (rect.x + tickSize + axisLineSize + scrollBarHeight); var length = axis.visibleLabels.length; var chartThemeStyle = this.chart.themeStyle; var count = 1; if (axis.valueType.indexOf('Category') > -1 && axis.labelPlacement === 'BetweenTicks' && length > 0 && !this.chart.stockChart) { length += 1; } var minorGridLines = axis.minorGridLines; var minorTickLines = axis.minorTickLines; //Gridlines for (var i = 0; i < length; i++) { tempInterval = !axis.visibleLabels[i] ? (axis.visibleLabels[i - 1].value + axis.visibleRange.interval) - ticksbwtLabel : axis.visibleLabels[i].value - ticksbwtLabel; pointY = valueToCoefficient(tempInterval, axis) * rect.height; pointY = (pointY * -1) + (rect.y + rect.height); if (pointY >= rect.y && (rect.y + rect.height) >= pointY) { if (this.chart.redraw && !this.chart.enableCanvas && this.chart.zoomRedraw && axis.visible && axis.majorGridLines.width && i !== 0 && !getElement(this.chart.element.id + '_MajorGridLine_' + index + '_' + i)) { majorGrid = 'M ' + this.seriesClipRect.x + ' ' + (this.seriesClipRect.y + (axis.isInversed ? this.seriesClipRect.height + ((this.seriesClipRect.height / (i ? i : 1)) * count) : -((this.seriesClipRect.height / (i ? i : 1)) * count))) + ' L ' + (this.seriesClipRect.x + this.seriesClipRect.width) + ' ' + (this.seriesClipRect.y + (axis.isInversed ? this.seriesClipRect.height + ((this.seriesClipRect.height / (i ? i : 1)) * count) : -((this.seriesClipRect.height / (i ? i : 1)) * count))); this.updateAxisElement(axis, index, majorGrid, i, '_MajorGridLine_', this.element, false); getElement(parent.id).appendChild(this.element.childNodes[this.element.childNodes.length - 1]); } if ((inside(tempInterval, axis.visibleRange)) || this.isBorder(axis, i, pointY)) { majorGrid = 'M ' + this.seriesClipRect.x + ' ' + (pointY) + ' L ' + (this.seriesClipRect.x + this.seriesClipRect.width) + ' ' + pointY; this.renderGridLine(axis, index, majorGrid, axis.majorGridLines, '_MajorGridLine_', i, this.element, chartThemeStyle.majorGridLine, axis.majorGridLines.dashArray); } if (this.chart.redraw && this.chart.zoomRedraw && axis.majorTickLines.width && i !== 0 && !getElement(this.chart.element.id + '_MajorTickLine_' + index + '_' + i) && !this.chart.enableCanvas && axis.visible) { majorTick = 'M ' + this.seriesClipRect.x + ' ' + (this.seriesClipRect.y + (axis.isInversed ? this.seriesClipRect.height + ((this.seriesClipRect.height / (i ? i : 1)) * count) : -((this.seriesClipRect.height / (i ? i : 1)) * count))) + ' L ' + ticks + ' ' + (this.seriesClipRect.y + (axis.isInversed ? this.seriesClipRect.height + ((this.seriesClipRect.height / (i ? i : 1)) * count) : -((this.seriesClipRect.height / (i ? i : 1)) * count))); this.updateAxisElement(axis, index, majorTick, i, '_MajorTickLine_', parent, false); getElement(parent.id).appendChild(this.element.childNodes[this.element.childNodes.length - 1]); count += 1; } majorTick = 'M ' + (rect.x + axisLineSize + (isTickInside ? scrollBarHeight : 0)) + ' ' + pointY + ' L ' + (ticks) + ' ' + pointY; this.renderGridLine(axis, index, majorTick, axis.majorTickLines, '_MajorTickLine_', i, parent, chartThemeStyle.majorTickLine); if ((minorGridLines.width > 0 || minorTickLines.width > 0) && axis.minorTicksPerInterval > 0) { if (i === 0 && isZoomSet(axis) && !isLogAxis && !isCategoryAxis) { this.renderMinorGridOnZooming(axis, tempInterval, rect, i, index, chartThemeStyle, parent); } minorGridDirection = this.drawAxisMinorLine(axis, tempInterval, rect, i); this.renderGridLine(axis, index, minorGridDirection[0], minorGridLines, '_MinorGridLine_', i, this.element, chartThemeStyle.minorGridLine, minorGridLines.dashArray); this.renderGridLine(axis, index, minorGridDirection[1], minorTickLines, '_MinorTickLine_', i, parent, chartThemeStyle.minorTickLine); if (i === length - 1 && isZoomSet(axis) && isLogAxis && !isCategoryAxis) { this.renderMinorGridOnZooming(axis, (tempInterval + axis.visibleRange.interval), rect, i, index, chartThemeStyle, parent); } } } } if (length && this.previousYLabel > length && !this.chart.enableCanvas && axis.visible && this.chart.zoomRedraw && this.chart.redraw) { for (var i = length; i < this.previousYLabel; i++) { var pointYValue = this.seriesClipRect.y + (axis.isInversed ? ((this.seriesClipRect.height / length) * ((i - length) + 1)) + this.seriesClipRect.height : -((this.seriesClipRect.height / length) * ((i - length) + 1))); if (axis.majorGridLines.width) { majorGrid = 'M ' + this.seriesClipRect.x + ' ' + +pointYValue + ' L ' + (this.seriesClipRect.x + this.seriesClipRect.width) + ' ' + pointYValue; this.updateAxisElement(axis, index, majorGrid, i, '_MajorGridLine_', this.element, true); } if (axis.majorTickLines.width) { majorTick = 'M ' + this.seriesClipRect.x + ' ' + pointYValue + ' L ' + ticks + ' ' + pointYValue; this.updateAxisElement(axis, index, majorTick, i, '_MajorTickLine_', parent, true); } } } }; /** * To check the border of the axis * * @param {Axis} axis axis * @param {number} index index * @param {number} value value * @returns {boolean} check the border of the axis */ CartesianAxisLayoutPanel.prototype.isBorder = function (axis, index, value) { var border = this.chart.chartArea.border; var rect = this.seriesClipRect; var orientation = axis.orientation; var start = (orientation === 'Horizontal') ? rect.x : rect.y; var size = (orientation === 'Horizontal') ? rect.width : rect.height; var startIndex = (orientation === 'Horizontal') ? 0 : axis.visibleLabels.length - 1; var endIndex = (orientation === 'Horizontal') ? axis.visibleLabels.length - 1 : 0; if (axis.plotOffset > 0) { return true; } else if ((value === start || value === (start + size)) && (border.width <= 0 || border.color === 'transparent')) { return true; } else if ((value !== start && index === startIndex) || (value !== (start + size) && index === endIndex)) { return true; } return false; }; /** * To render the yAxis label * * @param {Axis} axis axis * @param {number} index index * @param {Element} parent parent * @param {Rect} rect rect * @returns {void} * @private */ CartesianAxisLayoutPanel.prototype.drawYAxisLabels = function (axis, index, parent, rect) { var chart = this.chart; var label; var pointX = 0; var pointY = 0; var elementSize; var labelSpace = axis.labelPadding; var options; var isAxisBreakLabel; var isLabelInside = axis.labelPosition === 'Inside'; var isOpposed = axis.isAxisOpposedPosition; var RotatedWidth; var tickSpace = axis.labelPosition === axis.tickPosition ? axis.majorTickLines.height : 0; var padding = tickSpace + labelSpace + axis.lineStyle.width * 0.5; var angle = axis.angle % 360; var isVerticalAngle = (angle === -90 || angle === 90 || angle === 270 || angle === -270); padding += (isVerticalAngle) ? (isLabelInside ? 5 : -5) : 0; padding = (isOpposed) ? padding : -padding; var labelElement = chart.renderer.createGroup({ id: chart.element.id + 'AxisLabels' + index }); var scrollBarHeight = (isNullOrUndefined(axis.crossesAt) && axis.scrollbarSettings.position !== 'Left' && axis.scrollbarSettings.position !== 'Right') ? axis.scrollBarHeight * (isOpposed ? 1 : -1) : 0; var textHeight; var textPadding; var maxLineWidth; var pixel = 10; var isInverse = axis.isAxisInverse; var count = 1; var previousEnd = isInverse ? rect.y : (rect.y + rect.height); var labelPadding; var intervalLength; var labelHeight; var yAxisLabelX; var isLabelOnAxisLineLeft = ((!isOpposed && !isLabelInside) || (isOpposed && isLabelInside)); if (isLabelInside) { labelPadding = !isLabelOnAxisLineLeft ? -padding : padding; } else { labelPadding = !isLabelOnAxisLineLeft ? -padding + (chart.enableRtl ? -scrollBarHeight : scrollBarHeight) : padding + (chart.enableRtl ? -scrollBarHeight : scrollBarHeight); } var sizeWidth = []; var breakLabelSizeWidth = []; axis.visibleLabels.map(function (item) { sizeWidth.push(item.size['width']); breakLabelSizeWidth.push(item.breakLabelSize['width']); }); var LabelMaxWidth = Math.max.apply(Math, sizeWidth); var breakLabelMaxWidth = Math.max.apply(Math, breakLabelSizeWidth); RotatedWidth = LabelMaxWidth; if (angle >= -45 && angle <= 45 && angle !== 0) { RotatedWidth = LabelMaxWidth * Math.cos(angle * Math.PI / 180); if (RotatedWidth < 0) { RotatedWidth = -RotatedWidth; } } for (var i = 0, len = axis.visibleLabels.length; i < len; i++) { label = axis.visibleLabels[i]; isAxisBreakLabel = isBreakLabel(axis.visibleLabels[i].originalText); elementSize = isAxisBreakLabel ? axis.visibleLabels[i].breakLabelSize : axis.visibleLabels[i].size; pointY = (valueToCoefficient(axis.visibleLabels[i].value, axis) * rect.height) + (chart.stockChart && axis.labelPosition !== 'Outside' ? 7 : 0); pointY = Math.floor((pointY * -1) + (rect.y + rect.height)); textHeight = ((elementSize.height / 8) * axis.visibleLabels[i].text.length / 2); textPadding = (chart.requireInvertedAxis && axis.labelPosition === 'Inside') ? 0 : ((elementSize.height / 4) * 3) + 3; intervalLength = rect.height / axis.visibleLabels.length; labelHeight = ((axis.labelIntersectAction === 'Trim' || axis.labelIntersectAction === 'Wrap') && angle !== 0 && elementSize.width > intervalLength) ? intervalLength : elementSize.width; pointY = (isAxisBreakLabel ? (axis.labelPosition === 'Inside' ? (pointY - (elementSize.height / 2) - textHeight + textPadding) : (pointY - textHeight)) : (axis.labelPosition === 'Inside' ? pointY + textPadding : pointY)); if (axis.labelPosition === 'Inside' && ((i === 0 && !axis.isInversed) || (i === len - 1 && axis.isInversed))) { if (chart.stockChart) { pointY -= (textPadding); } else { pointY -= (textPadding - ((chart.requireInvertedAxis && axis.labelPosition === 'Inside') ? 0 : (axis.opposedPosition ? -padding : padding))); } } if (axis.majorGridLines.width > axis.majorTickLines.width) { maxLineWidth = axis.majorGridLines.width; } else { maxLineWidth = axis.majorTickLines.width; } if (axis.labelStyle.textAlignment === 'Far') { pointY = pointY - maxLineWidth - pixel; } else if (axis.labelStyle.textAlignment === 'Near') { pointY = pointY + maxLineWidth + pixel; } // label X value adjustment (Start) if (isLabelInside) { yAxisLabelX = labelPadding + ((angle === 0 ? elementSize.width : (isAxisBreakLabel ? breakLabelMaxWidth : LabelMaxWidth)) / 2); } else { yAxisLabelX = labelPadding - ((angle === 0 ? elementSize.width : (isAxisBreakLabel ? breakLabelMaxWidth : RotatedWidth)) / 2); } if (axis.enableWrap && chart.requireInvertedAxis && angle && ((!axis.opposedPosition && axis.labelPosition === 'Inside') || (axis.opposedPosition && axis.labelPosition === 'Outside'))) { yAxisLabelX = axis.opposedPosition ? yAxisLabelX - LabelMaxWidth / 2 : yAxisLabelX + LabelMaxWidth / 2; } pointX = isOpposed ? (axis.scrollBarHeight !== 0 && axis.scrollbarSettings.position !== 'Right' && axis.scrollbarSettings.position !== 'Left') ? ((rect.x + axis.scrollBarHeight + padding) - yAxisLabelX) : (rect.x - yAxisLabelX) : (rect.x + yAxisLabelX); if (isVerticalAngle) { pointX += (isOpposed) ? 5 : -5; } yAxisLabelX = labelPadding; options = new TextOption(chart.element.id + index + '_AxisLabel_' + i, pointX, pointY, 'middle', label.text, '', 'middle', angle); switch (axis.edgeLabelPlacement) { case 'None': break; case 'Hide': if (((i === 0 || (isInverse && i === len - 1)) && options.y > rect.y) || (((i === len - 1) || (isInverse && i === 0)) && options.y - elementSize.height * 0.5 < rect.y)) { options.text = ''; } break; case 'Shift': if ((i === 0 || (isInverse && i === len - 1)) && options.y > rect.y + rect.height) { options.y = pointY = rect.y + rect.height; } else if (((i === len - 1) || (isInverse && i === 0)) && (options.y <= 0)) { options.y = pointY = rect.y + elementSize.height * 0.5; } break; } // ------- Hide Calculation (Start) ------------- var previousYValue = options.y; var currentYValue = options.y - labelHeight; if (isAxisBreakLabel) { previousYValue = (options.y - (labelHeight / 2)); currentYValue = options.y + (labelHeight / 2); } if ((angle === 90 || angle === 270) && axis.labelIntersectAction === 'Hide' && i !== 0 && (!isInverse ? previousYValue >= previousEnd : currentYValue <= previousEnd)) { continue; } previousEnd = isInverse ? previousYValue : currentYValue; // ------- Hide Calculation (End) ------------- options.transform = 'rotate(' + angle + ',' + pointX + ',' + pointY + ')'; if (this.chart.redraw && this.chart.zoomRedraw && !getElement(options.id) && !this.chart.enableCanvas && axis.visible) { var optionsY = options.y; options.y = this.seriesClipRect.y + (axis.isInversed ? this.seriesClipRect.height + ((this.seriesClipRect.height / (i ? i : 1)) * count) : -((this.seriesClipRect.height / (i ? i : 1)) * count)); this.updateAxisElement(axis, index, '', i, '_AxisLabel_', labelElement, false, options, label); options.y = optionsY; count += 1; } textElement(chart.renderer, options, label.labelStyle, label.labelStyle.color || chart.themeStyle.axisLabelFont.color, labelElement, false, chart.redraw, true, true, chart.du