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.

630 lines (629 loc) 35.8 kB
import { Animation, Browser, createElement } from '@syncfusion/ej2-base'; import { textElement, getValueXByPoint, stopTimer, findCrosshairDirection, getValueYByPoint, ChartLocation, withInBounds, removeElement, convertHexToColor, colorNameToHex } from '../../common/utils/helper'; import { PathOption, Rect, TextOption, measureText, SvgRenderer } from '@syncfusion/ej2-svg-base'; import { ChartData } from '../../chart/utils/get-data'; /** * The `Crosshair` module is used to render the crosshair for the chart. */ var Crosshair = /** @class */ (function () { /** * Constructor for crosshair module. * * @private */ function Crosshair(chart) { this.arrowLocation = new ChartLocation(0, 0); this.rx = 2; this.ry = 2; this.highlightWidth = 0; this.crosshairLeftOverflow = 0; this.crosshairRightOverflow = 0; this.chart = chart; this.elementID = this.chart.element.id; this.svgRenderer = new SvgRenderer(this.chart.element.id); if (this.chart.crosshair.snapToData || this.chart.crosshair.highlightCategory) { this.data = new ChartData(this.chart); } this.addEventListener(); } /** * Adds event listeners to the chart elements. * * @private * @returns {void} */ Crosshair.prototype.addEventListener = function () { if (this.chart.isDestroyed) { return; } var cancelEvent = Browser.isPointer ? 'pointerleave' : 'mouseleave'; this.chart.on(Browser.touchMoveEvent, this.mouseMoveHandler, this); this.chart.on(Browser.touchEndEvent, this.mouseUpHandler, this); this.chart.on(cancelEvent, this.mouseLeaveHandler, this); this.chart.on('tapHold', this.longPress, this); }; Crosshair.prototype.mouseUpHandler = function () { if (this.chart.startMove) { this.removeCrosshair(2000); } }; Crosshair.prototype.mouseLeaveHandler = function () { this.removeCrosshair(1000); }; Crosshair.prototype.mouseMoveHandler = function (event) { var chart = this.chart; if (chart.stockChart && chart.stockChart.onPanning) { if (chart.mouseY < chart.chartAxisLayoutPanel.seriesClipRect.y) { chart.mouseY = chart.chartAxisLayoutPanel.seriesClipRect.y; } else if (chart.mouseY > chart.chartAxisLayoutPanel.seriesClipRect.y + chart.chartAxisLayoutPanel.seriesClipRect.height) { chart.mouseY = chart.chartAxisLayoutPanel.seriesClipRect.y + chart.chartAxisLayoutPanel.seriesClipRect.height; } } if (event.type === 'touchmove' && (Browser.isIos || Browser.isIos7) && chart.startMove && event.preventDefault) { event.preventDefault(); } // Tooltip for chart series. if (!chart.disableTrackTooltip) { if (chart.crosshair.enable && withInBounds(chart.mouseX, chart.mouseY, chart.chartAxisLayoutPanel.seriesClipRect)) { if (chart.startMove || !chart.isTouch) { this.crosshair(); } } else { this.removeCrosshair(1000); } } }; /** * Handles the long press on chart. * * @returns {boolean} false * @private */ Crosshair.prototype.longPress = function () { var chart = this.chart; if (withInBounds(chart.mouseX, chart.mouseY, chart.chartAxisLayoutPanel.seriesClipRect)) { this.crosshair(); } return false; }; /** * Finds the data points closest to the mouse position for all visible series in the chart. * Updates the `data` object with the nearest data point to be used for mouse interactions. * * @param {Chart} chart - The chart instance containing the visible series and mouse position. * @returns {boolean} - True if chart has atleast one visible series. * @private */ Crosshair.prototype.findMousePoints = function (chart) { var data = this.data.getData(); var pointLocations = []; var axisCoordinate = chart.isTransposed ? 'x' : 'y'; var nearestDataPoint = null; var nearestValue = null; var minDifference = Infinity; // For finding the nearest value var mouseCoordinate = axisCoordinate === 'x' ? chart.mouseX : chart.mouseY; var seriesVisibility = false; var commonXvalues = this.data.mergeXvalues(this.chart.visibleSeries); for (var _i = 0, _a = chart.visibleSeries; _i < _a.length; _i++) { var series = _a[_i]; seriesVisibility = seriesVisibility ? seriesVisibility : series.visible; if (series.visible && series.category !== 'TrendLine') { // Get the closest X value and store the data point var closestData = this.data.getClosestX(chart, series, commonXvalues) || data; if (closestData && closestData.point && closestData.point.symbolLocations[0]) { var pointLocation = closestData.point.symbolLocations[0][axisCoordinate] + closestData.series.clipRect[axisCoordinate]; if (chart.crosshair.snapToData || chart.crosshair.highlightCategory) { pointLocations.push(pointLocation); // Store point locations for nearest calculation // Calculate the nearest point to the mouse var difference = Math.abs(pointLocation - mouseCoordinate); if (difference < minDifference) { minDifference = difference; nearestValue = pointLocation; nearestDataPoint = closestData; } } } } } // Use the nearest data point if ((chart.crosshair.snapToData || chart.crosshair.highlightCategory) && nearestDataPoint) { data = nearestDataPoint; chart.crosshairModule.highlightWidth = 0; if (chart.crosshair.highlightCategory && data.series.xAxis.valueType === 'Category') { var clipRectSize = chart.isTransposed || chart.requireInvertedAxis ? chart.chartAxisLayoutPanel.seriesClipRect.height : chart.chartAxisLayoutPanel.seriesClipRect.width; var highlightCategoryWidth = clipRectSize / data.series.xAxis.visibleRange.delta; var pointRelativePosition = (data.point.xValue - data.series.xAxis.visibleRange.min) / (data.series.xAxis.visibleRange.max - data.series.xAxis.visibleRange.min); chart.crosshairModule.crosshairLeftOverflow = Math.max(0, (highlightCategoryWidth / 2) - pointRelativePosition * clipRectSize); chart.crosshairModule.crosshairRightOverflow = Math.max(0, (pointRelativePosition * clipRectSize + (highlightCategoryWidth / 2)) - clipRectSize); chart.crosshairModule.highlightWidth = Math.max(0, highlightCategoryWidth - chart.crosshairModule.crosshairLeftOverflow - chart.crosshairModule.crosshairRightOverflow); } } if (data && data.point) { this.data.findMouseValues(data, chart, this); } return seriesVisibility; }; /** * Renders the crosshair. * * @returns {void} * @private */ Crosshair.prototype.crosshair = function () { var visibleSeriesLength = this.chart.series.filter(function (series) { return series.visible; }).length; var seriesVisible = true; if (this.chart.crosshair.snapToData || this.chart.crosshair.highlightCategory) { seriesVisible = this.findMousePoints(this.chart); } var chart = this.chart; var horizontalCross = ''; var verticalCross = ''; var options; var axisTooltipGroup = document.getElementById(this.elementID + '_crosshair_axis'); var crosshair = chart.crosshair; var tooltipdiv = document.getElementById(this.elementID + '_tooltip'); var chartRect = chart.chartAxisLayoutPanel.seriesClipRect; var crossGroup = chart.enableCanvas ? document.getElementById(this.elementID + '_Secondary_Element') : document.getElementById(this.elementID + '_UserInteraction'); var crosshairsvg; var cross = document.getElementById(this.elementID + '_Crosshair'); if (chart.enableCanvas) { if (!cross) { cross = createElement('div', { id: this.elementID + '_Crosshair', styles: 'position: absolute; pointer-events: none' }); crossGroup.appendChild(cross); } } this.stopAnimation(); if (visibleSeriesLength === 0 || (chart.crosshair.snapToData && this.valueY === undefined) || chart.isCrosshair && chart.tooltip.enable && chart.tooltipModule && !withInBounds(chart.tooltipModule.valueX, chart.tooltipModule.valueY, chartRect) || (chart.crosshair.snapToData && !seriesVisible)) { return null; } this.valueX = chart.crosshair.snapToData || chart.crosshair.highlightCategory ? this.valueX : (chart.tooltip.enable && chart.tooltipModule && chart.tooltipModule.valueX ? chart.tooltipModule.valueX : chart.mouseX); this.valueY = chart.crosshair.snapToData || chart.crosshair.highlightCategory ? this.valueY : (chart.tooltip.enable && chart.tooltipModule && chart.tooltipModule.valueY ? chart.tooltipModule.valueY : chart.mouseY); if (!chart.enableCanvas && crossGroup) { crossGroup.setAttribute('opacity', '1'); } if (crosshair.lineType === 'Both' || crosshair.lineType === 'Horizontal') { if ((chart.crosshair.highlightCategory && this.highlightWidth !== 0) && (chart.isTransposed || chart.requireInvertedAxis)) { var crosshairHighlightWidth = this.highlightWidth; chart.crosshairModule.valueY = this.adjustCrosshairPositionForOverflow(this.valueY, true, chart); var y = this.valueY - (crosshairHighlightWidth / 2); horizontalCross = 'M ' + chartRect.x + ' ' + y + ' L ' + (chartRect.x + chartRect.width) + ' ' + y + ' L ' + (chartRect.x + chartRect.width) + ' ' + (y + crosshairHighlightWidth) + ' L ' + chartRect.x + ' ' + (y + crosshairHighlightWidth) + ' Z'; } else { horizontalCross += 'M ' + chartRect.x + ' ' + this.valueY + ' L ' + (chartRect.x + chartRect.width) + ' ' + this.valueY; } } if (crosshair.lineType === 'Both' || crosshair.lineType === 'Vertical') { if ((chart.crosshair.highlightCategory && this.highlightWidth !== 0) && !chart.requireInvertedAxis) { var crosshairHighlightWidth = this.highlightWidth; chart.crosshairModule.valueX = this.adjustCrosshairPositionForOverflow(this.valueX, false, chart); var x = this.valueX - (crosshairHighlightWidth / 2); verticalCross = 'M ' + x + ' ' + chartRect.y + ' L ' + (x + crosshairHighlightWidth) + ' ' + chartRect.y + ' L ' + (x + crosshairHighlightWidth) + ' ' + (chartRect.y + chartRect.height) + ' L ' + x + ' ' + (chartRect.y + chartRect.height) + ' Z'; } else { verticalCross += 'M ' + this.valueX + ' ' + chartRect.y + ' L ' + this.valueX + ' ' + (chartRect.y + chartRect.height); } } if (chart.enableCanvas) { if (!axisTooltipGroup) { axisTooltipGroup = this.svgRenderer.createGroup({ 'id': this.elementID + '_crosshair_axis' }); } var elementID = chart.tooltip.enable ? chart.element.id + '_tooltip_svg' : chart.element.id + '_svg'; crosshairsvg = this.svgRenderer.createSvg({ id: elementID, width: chart.availableSize.width, height: chart.availableSize.height }); if (chart.tooltip.enable) { tooltipdiv = !tooltipdiv ? chart.tooltipModule.createElement() : tooltipdiv; tooltipdiv.appendChild(crosshairsvg); crossGroup.appendChild(tooltipdiv); } options = new PathOption(this.elementID + '_HorizontalLine', 'none', crosshair.line.width, crosshair.horizontalLineColor || crosshair.line.color || chart.themeStyle.crosshairLine, crosshair.opacity, (chart.theme.indexOf('Bootstrap5') > -1 || chart.theme === 'Fluent2HighContrast' || chart.theme.indexOf('Tailwind3') > -1) ? crosshair.dashArray || '2.5' : crosshair.dashArray, horizontalCross); this.drawCrosshairLine(options, cross, chartRect.x, this.valueY, chartRect.width, 0, horizontalCross); /** * due to not working for vertical line side I added new option * options.d = verticalCross; options.id = this.elementID + '_VerticalLine'; */ options = new PathOption(this.elementID + '_VerticalLine', 'none', crosshair.line.width, crosshair.verticalLineColor || crosshair.line.color || chart.themeStyle.crosshairLine, crosshair.opacity, (chart.theme.indexOf('Bootstrap5') > -1 || chart.theme === 'Fluent2HighContrast' || chart.theme.indexOf('Tailwind3') > -1) ? crosshair.dashArray || '2.5' : crosshair.dashArray, verticalCross); this.drawCrosshairLine(options, cross, this.valueX, chartRect.y, 0, chartRect.height, verticalCross); this.renderAxisTooltip(chart, chartRect, axisTooltipGroup); crosshairsvg.appendChild(axisTooltipGroup); if (!chart.tooltip.enable) { cross.appendChild(crosshairsvg); } } else { if (crossGroup.childNodes.length === 0) { var horizontalHighlight = (chart.crosshair.highlightCategory && this.highlightWidth !== 0) && (chart.isTransposed || chart.requireInvertedAxis); var verticalHighlight = (chart.crosshair.highlightCategory && this.highlightWidth !== 0) && !chart.requireInvertedAxis; axisTooltipGroup = chart.renderer.createGroup({ 'id': this.elementID + '_crosshair_axis' }); options = new PathOption(this.elementID + '_HorizontalLine', horizontalHighlight ? (crosshair.horizontalLineColor || crosshair.line.color) ? this.crosshairLightenColor(crosshair.horizontalLineColor || crosshair.line.color) : chart.themeStyle.crosshairBackground : 'none', horizontalHighlight ? 0 : crosshair.line.width, horizontalHighlight ? 'none' : crosshair.horizontalLineColor || crosshair.line.color || chart.themeStyle.crosshairLine, crosshair.opacity, horizontalHighlight ? null : (chart.theme.indexOf('Bootstrap5') > -1 || chart.theme === 'Fluent2HighContrast' || chart.theme.indexOf('Tailwind3') > -1) ? crosshair.dashArray || '2.5' : crosshair.dashArray, horizontalCross); this.renderCrosshairLine(options, crossGroup); options = new PathOption(this.elementID + '_VerticalLine', verticalHighlight ? (crosshair.verticalLineColor || crosshair.line.color) ? this.crosshairLightenColor(crosshair.verticalLineColor || crosshair.line.color) : chart.themeStyle.crosshairBackground : 'none', verticalHighlight ? 0 : crosshair.line.width, verticalHighlight ? 'none' : crosshair.verticalLineColor || crosshair.line.color || chart.themeStyle.crosshairLine, crosshair.opacity, verticalHighlight ? null : (chart.theme.indexOf('Bootstrap5') > -1 || chart.theme === 'Fluent2HighContrast' || chart.theme.indexOf('Tailwind3') > -1) ? crosshair.dashArray || '2.5' : crosshair.dashArray, verticalCross); this.renderCrosshairLine(options, crossGroup); crossGroup.appendChild(axisTooltipGroup); this.renderAxisTooltip(chart, chartRect, crossGroup.lastChild); } else { document.getElementById(this.elementID + '_HorizontalLine').setAttribute('d', horizontalCross); document.getElementById(this.elementID + '_VerticalLine').setAttribute('d', verticalCross); this.renderAxisTooltip(chart, chartRect, crossGroup.lastChild); } } }; /** * Converts a specified color into a semi-transparent RGB string format. * * @param {string} color - The main color in hex format. * @returns {string} - The lightened color in RGBA format with an alpha value of 0.25. */ Crosshair.prototype.crosshairLightenColor = function (color) { var rgbValue = convertHexToColor(colorNameToHex(color)); return 'rgb(' + rgbValue.r + ',' + rgbValue.g + ',' + rgbValue.b + ',' + 0.25 + ')'; }; /** * Adjusts the crosshair position to account for any overflow beyond the chart boundaries, * ensuring it stays within visible limits. It handles horizontal and vertical orientations separately. * * @param {number} initialPosition - The initial calculated position of the crosshair before adjustments. * @param {boolean} isHorizontalOrientation - Determines whether the crosshair is oriented horizontally. * @param {Chart} chart - The chart instance containing details on crosshair module and overflow values. * @returns {number} - The adjusted position of the crosshair after accounting for boundary overflow. * @private */ Crosshair.prototype.adjustCrosshairPositionForOverflow = function (initialPosition, isHorizontalOrientation, chart) { if (chart.crosshairModule.crosshairLeftOverflow > 0) { initialPosition += isHorizontalOrientation ? -chart.crosshairModule.crosshairLeftOverflow / 2 : chart.crosshairModule.crosshairLeftOverflow / 2; } if (chart.crosshairModule.crosshairRightOverflow > 0) { initialPosition += isHorizontalOrientation ? chart.crosshairModule.crosshairRightOverflow / 2 : -chart.crosshairModule.crosshairRightOverflow / 2; } return initialPosition; }; Crosshair.prototype.renderCrosshairLine = function (options, crossGroup) { var htmlObject = this.chart.renderer.drawPath(options); crossGroup.appendChild(htmlObject); }; Crosshair.prototype.drawCrosshairLine = function (options, crossGroup, left, top, width, height, direction) { if (!document.getElementById(options.id) && direction) { var line = createElement('div', { id: options.id }); crossGroup.appendChild(line); } if (document.getElementById(options.id)) { var style = 'top:' + top.toString() + 'px;' + 'left:' + left.toString() + 'px;' + 'width:' + width + 'px;' + 'height:' + height + 'px;' + 'fill:' + options.stroke + ';' + 'border: 0.5px solid ' + options.stroke + ';' + 'opacity: ' + options.opacity + ' ; ' + 'position: absolute'; var crosshairline = document.getElementById(options.id); var crosshairtooltip = document.getElementById(this.elementID + '_crosshair_axis'); crosshairline.style.cssText = style; crossGroup.style.opacity = '1'; if (crosshairtooltip) { crosshairtooltip.style.opacity = '1'; } } }; Crosshair.prototype.renderAxisTooltip = function (chart, chartRect, axisGroup) { var axis; var text; var rect; var pathElement; var textElem; var options; var padding = 5; var direction; var axisRect; for (var k = 0, length_1 = chart.axisCollections.length; k < length_1; k++) { axis = chart.axisCollections[k]; axisRect = !axis.placeNextToAxisLine ? axis.rect : axis.updatedRect; if (axis.crosshairTooltip.enable) { if (axisRect && ((this.valueX <= (axisRect.x + axisRect.width) && axisRect.x <= this.valueX) || (this.valueY <= (axisRect.y + axisRect.height) && axisRect.y <= this.valueY))) { pathElement = document.getElementById(this.elementID + '_axis_tooltip_' + k); textElem = document.getElementById(this.elementID + '_axis_tooltip_text_' + k); text = this.getAxisText(axis); if (text && text.indexOf('<br') > -1) { text = this.getAxisText(axis).split(/<br.*?>/g); } if (!text) { continue; } rect = this.tooltipLocation(text, axis, chartRect, axisRect); if (rect.y + rect.height / 2 > chart.availableSize.height || rect.y < 0) { continue; } if (pathElement === null) { if (chart.enableCanvas) { pathElement = this.svgRenderer.drawPath({ 'id': this.elementID + '_axis_tooltip_' + k, 'fill': axis.crosshairTooltip.fill || chart.themeStyle.crosshairFill }); } else { pathElement = chart.renderer.drawPath({ 'id': this.elementID + '_axis_tooltip_' + k, 'fill': axis.crosshairTooltip.fill || chart.themeStyle.crosshairFill }, null); } axisGroup.appendChild(pathElement); options = new TextOption(this.elementID + '_axis_tooltip_text_' + k, 0, 0, (chart.stockChart && chart.enableRtl) ? 'end' : 'start', text); var render = chart.enableCanvas ? this.svgRenderer : chart.renderer; textElem = textElement(render, options, axis.crosshairTooltip.textStyle, axis.crosshairTooltip.textStyle.color || chart.themeStyle.crosshairLabelFont.color, axisGroup, null, null, null, null, null, null, null, null, chart.enableCanvas, null, this.chart.themeStyle.crosshairLabelFont); } direction = findCrosshairDirection(this.rx, this.ry, rect, this.arrowLocation, 9, this.isTop, this.isBottom, this.isLeft, this.valueX, this.valueY); pathElement.setAttribute('d', direction); if (typeof text !== 'string' && text.length > 1) { for (var i = 0; i < text.length; i++) { textElem.childNodes[i].textContent = text[i]; } } else { textElem.textContent = text; } textElem.setAttribute('x', (rect.x + padding + (chart.enableRtl ? this.elementSize.width : 0)).toString()); textElem.setAttribute('y', (rect.y + padding + 3 * this.elementSize.height / 4).toString()); var shadowId = this.chart.element.id + '_shadow'; if (typeof text !== 'string' && text.length > 1) { var height = 0; textElem.setAttribute('y', (rect.y + padding + 3 * this.elementSize.height / (4 * text.length)).toString()); for (var i = 0; i < textElem.children.length; i++) { height += this.elementSize.height / text.length; textElem.children[i].setAttribute('x', (rect.x + padding + (chart.enableRtl ? this.elementSize.width : 0) + this.elementSize.width / 2).toString()); textElem.children[i].setAttribute('y', ((parseInt(textElem.getAttribute('y'), 10) + height).toString())); textElem.children[i].style.textAnchor = 'middle'; } } if (this.chart.theme === 'Fluent' || this.chart.theme === 'FluentDark' || this.chart.theme === 'Fabric' || this.chart.theme === 'FabricDark' || this.chart.theme === 'Fluent2HighContrast') { var defElement = this.chart.renderer.createDefs(); var bordercolor = this.chart.theme === 'Fluent' || this.chart.theme === 'Fabric' ? '#D2D0CE' : this.chart.theme === 'Fluent2HighContrast' ? '#FFFFFF' : null; var borderwidth = this.chart.theme === 'Fluent' || this.chart.theme === 'Fabric' || this.chart.theme === 'Fluent2HighContrast' ? 1 : null; defElement.setAttribute('id', this.chart.element.id + 'SVG_tooltip_definition'); axisGroup.appendChild(defElement); pathElement.setAttribute('stroke', bordercolor); pathElement.setAttribute('stroke-width', ' ' + borderwidth); } else if (this.chart.theme.indexOf('Fluent2') > -1) { pathElement.setAttribute('box-shadow', '0px 1.6px 3.6px 0px #00000021, 0px 0.3px 0.9px 0px #0000001A'); pathElement.setAttribute('filter', Browser.isIE ? '' : 'url(#' + shadowId + ')'); var shadow = '<filter id="' + shadowId + '" height="130%"><feGaussianBlur in="SourceAlpha" stdDeviation="3"/>'; shadow += '<feOffset dx="-1" dy="3.6" result="offsetblur"/><feComponentTransfer><feFuncA type="linear" slope="0.2"/>'; shadow += '</feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter>'; var defElement = this.chart.renderer.createDefs(); defElement.setAttribute('id', this.chart.element.id + 'SVG_tooltip_definition'); pathElement.appendChild(defElement); defElement.innerHTML = shadow; } } else { removeElement(this.elementID + '_axis_tooltip_' + k); removeElement(this.elementID + '_axis_tooltip_text_' + k); } } } }; Crosshair.prototype.getAxisText = function (axis) { var value; this.isBottom = false; this.isTop = false; this.isLeft = false; this.isRight = false; var labelValue = (axis.valueType === 'Category' && axis.labelPlacement === 'BetweenTicks') ? 0.5 : 0; var isOpposed = axis.isAxisOpposedPosition; if (axis.orientation === 'Horizontal') { value = getValueXByPoint(Math.abs(this.valueX - axis.rect.x), axis.rect.width, axis) + labelValue; this.isBottom = !isOpposed; this.isTop = isOpposed; } else { value = getValueYByPoint(Math.abs(this.valueY - axis.rect.y), axis.rect.height, axis) + labelValue; this.isRight = isOpposed; this.isLeft = !isOpposed; } if (axis.valueType === 'DateTime') { return axis.format(new Date(value)); } else if (axis.valueType === 'Category') { return axis.labels[Math.floor(value)]; } else if (axis.valueType === 'DateTimeCategory') { return this.chart.dateTimeCategoryModule.getIndexedAxisLabel(axis.labels[Math.round(value)], axis.format); } else if (axis.valueType === 'Logarithmic') { return value = axis.format(Math.pow(axis.logBase, value)); } else { var customLabelFormat = axis.labelFormat && axis.labelFormat.match('{value}') !== null; return customLabelFormat ? axis.labelFormat.replace('{value}', axis.format(value)) : axis.format(value); } }; Crosshair.prototype.tooltipLocation = function (text, axis, bounds, axisRect) { var padding = 5; var arrowPadding = 9; var tooltipRect; var boundsX = bounds.x; var boundsY = bounds.y; var islabelInside = axis.labelPosition === 'Inside'; var scrollBarHeight = axis.scrollbarSettings.enable || (axis.zoomingScrollBar && axis.zoomingScrollBar.svgObject) ? axis.scrollBarHeight : 0; this.elementSize = measureText(text, axis.crosshairTooltip.textStyle, this.chart.themeStyle.crosshairLabelFont); if (typeof text !== 'string' && text.length > 1) { this.elementSize.width = 0; this.elementSize.height = 0; for (var i = 0; i < text.length; i++) { var size = measureText(text[i], axis.crosshairTooltip.textStyle, this.chart.themeStyle.crosshairLabelFont); this.elementSize.height += size.height; if (this.elementSize.width < size.width) { this.elementSize.width = size.width; } } } var isOpposed = axis.isAxisOpposedPosition; if (axis.orientation === 'Horizontal') { var yLocation = islabelInside ? axisRect.y - this.elementSize.height - (padding * 2 + arrowPadding) : axisRect.y + scrollBarHeight; var height = islabelInside ? axisRect.y - this.elementSize.height - arrowPadding : axisRect.y + arrowPadding; this.arrowLocation = new ChartLocation(this.valueX, yLocation); tooltipRect = new Rect((this.valueX - (this.elementSize.width / 2) - padding), height + (!islabelInside ? scrollBarHeight : 0), this.elementSize.width + padding * 2, this.elementSize.height + padding * 2); if (isOpposed) { tooltipRect.y = islabelInside ? axisRect.y : axisRect.y - (this.elementSize.height + padding * 2 + arrowPadding) - scrollBarHeight; } if (tooltipRect.x < boundsX) { tooltipRect.x = boundsX; } if (tooltipRect.x + tooltipRect.width > boundsX + bounds.width) { tooltipRect.x -= ((tooltipRect.x + tooltipRect.width) - (boundsX + bounds.width)); } if (this.arrowLocation.x + arrowPadding / 2 > tooltipRect.x + tooltipRect.width - this.rx) { this.arrowLocation.x = tooltipRect.x + tooltipRect.width - this.rx - arrowPadding; } if (this.arrowLocation.x - arrowPadding < tooltipRect.x + this.rx) { this.arrowLocation.x = tooltipRect.x + this.rx + arrowPadding; } } else { scrollBarHeight = scrollBarHeight * (isOpposed ? 1 : -1); this.arrowLocation = new ChartLocation(axisRect.x, this.valueY); var width = islabelInside ? axisRect.x - scrollBarHeight : axisRect.x - (this.elementSize.width) - (padding * 2 + arrowPadding); tooltipRect = new Rect(width + scrollBarHeight, this.valueY - (this.elementSize.height / 2) - padding, this.elementSize.width + (padding * 2), this.elementSize.height + padding * 2); if (isOpposed) { tooltipRect.x = islabelInside ? axisRect.x - this.elementSize.width - arrowPadding : axisRect.x + arrowPadding + scrollBarHeight; if ((tooltipRect.x + tooltipRect.width) > this.chart.availableSize.width) { this.arrowLocation.x -= ((tooltipRect.x + tooltipRect.width) - this.chart.availableSize.width); tooltipRect.x -= ((tooltipRect.x + tooltipRect.width) - this.chart.availableSize.width); } } else { if (tooltipRect.x < 0) { this.arrowLocation.x -= tooltipRect.x; tooltipRect.x = 0; } } if (tooltipRect.y < boundsY) { tooltipRect.y = boundsY; } if (tooltipRect.y + tooltipRect.height >= boundsY + bounds.height) { tooltipRect.y -= ((tooltipRect.y + tooltipRect.height) - (boundsY + bounds.height)); } if (this.arrowLocation.y + arrowPadding / 2 > tooltipRect.y + tooltipRect.height - this.ry) { this.arrowLocation.y = tooltipRect.y + tooltipRect.height - this.ry - arrowPadding / 2; } if (this.arrowLocation.y - arrowPadding / 2 < tooltipRect.y + this.ry) { this.arrowLocation.y = tooltipRect.y + this.ry + arrowPadding / 2; } } return tooltipRect; }; Crosshair.prototype.stopAnimation = function () { stopTimer(this.crosshairInterval); }; Crosshair.prototype.progressAnimation = function () { stopTimer(this.crosshairInterval); }; /** * Removes the crosshair on mouse leave. * * @returns {void} * @private */ Crosshair.prototype.removeCrosshair = function (duration) { var chart = this.chart; var crosshair = chart.enableCanvas ? document.getElementById(this.elementID + '_Crosshair') : document.getElementById(this.elementID + '_UserInteraction'); var crosshairtooltip = chart.enableCanvas ? document.getElementById(this.elementID + '_crosshair_axis') : null; this.stopAnimation(); if (crosshair && crosshair.getAttribute('opacity') !== '0') { this.crosshairInterval = +setTimeout(function () { new Animation({}).animate(crosshair, { duration: 200, progress: function (args) { // crosshair.removeAttribute('e-animate'); crosshair.style.animation = ''; if (!chart.enableCanvas) { crosshair.setAttribute('opacity', (1 - (args.timeStamp / args.duration)).toString()); } else { crosshair.style.opacity = (1 - (args.timeStamp / args.duration)).toString(); crosshairtooltip.style.opacity = (1 - (args.timeStamp / args.duration)).toString(); } }, end: function () { if (chart.enableCanvas) { crosshair.style.opacity = '0'; crosshairtooltip.style.opacity = '0'; } else { crosshair.setAttribute('opacity', '0'); } chart.startMove = false; if (chart.tooltipModule) { chart.tooltipModule.valueX = null; chart.tooltipModule.valueY = null; } } }); }, duration); } }; /** * Get module name. * * @returns {string} module name */ Crosshair.prototype.getModuleName = function () { /** * Returns the module name. */ return 'Crosshair'; }; /** * To destroy the crosshair. * * @returns {void} * @private */ Crosshair.prototype.destroy = function () { /** * Destroy method performed here. */ }; return Crosshair; }()); export { Crosshair };