diff --git a/src/charts/Bar.js b/src/charts/Bar.js index 2d4a76e00..77adb27e8 100644 --- a/src/charts/Bar.js +++ b/src/charts/Bar.js @@ -125,8 +125,10 @@ class Bar { let barHeight = 0 let barWidth = 0 + let translationsIndex = 0 if (this.yRatio.length > 1) { - this.yaxisIndex = realIndex + this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex] + translationsIndex = realIndex } this.isReversed = @@ -204,7 +206,8 @@ class Bar { barWidth, zeroH, }) - barHeight = this.series[i][j] / this.yRatio[this.yaxisIndex] + let translationsIndex = this.yRatio.length > 1 ? realIndex : 0 + barHeight = this.series[i][j] / this.yRatio[translationsIndex] } let pathFill = this.barHelpers.getPathFillColor(series, i, j, realIndex) diff --git a/src/charts/BarStacked.js b/src/charts/BarStacked.js index 9c49c0eb2..5560127a4 100644 --- a/src/charts/BarStacked.js +++ b/src/charts/BarStacked.js @@ -61,8 +61,10 @@ class BarStacked extends Bar { let realIndex = w.globals.comboCharts ? seriesIndex[i] : i + let translationsIndex = 0 if (this.yRatio.length > 1) { - this.yaxisIndex = realIndex + this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex][0] + translationsIndex = realIndex } this.isReversed = @@ -150,7 +152,7 @@ class BarStacked extends Bar { barWidth, zeroH, }) - barHeight = this.series[i][j] / this.yRatio[this.yaxisIndex] + barHeight = this.series[i][j] / this.yRatio[translationsIndex] } const barGoalLine = this.barHelpers.drawGoalLine({ diff --git a/src/charts/BoxCandleStick.js b/src/charts/BoxCandleStick.js index b0e2a0145..5a06a0e6e 100644 --- a/src/charts/BoxCandleStick.js +++ b/src/charts/BoxCandleStick.js @@ -66,8 +66,10 @@ class BoxCandleStick extends Bar { let barHeight = 0 let barWidth = 0 + let translationsIndex = 0 if (this.yRatio.length > 1) { - this.yaxisIndex = realIndex + this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex][0] + translationsIndex = realIndex } let initPositions = this.barHelpers.initialPositions() @@ -98,7 +100,8 @@ class BoxCandleStick extends Bar { indexes: { i, j, - realIndex + realIndex, + translationsIndex }, x, y, @@ -201,7 +204,7 @@ class BoxCandleStick extends Bar { color = [this.boxOptions.colors.lower, this.boxOptions.colors.upper] } - const yRatio = this.yRatio[this.yaxisIndex] + const yRatio = this.yRatio[indexes.translationsIndex] let realIndex = indexes.realIndex const ohlc = this.getOHLCValue(realIndex, j) diff --git a/src/charts/Line.js b/src/charts/Line.js index bed2a31c5..773f1fe07 100644 --- a/src/charts/Line.js +++ b/src/charts/Line.js @@ -62,6 +62,8 @@ class Line { series = this.lineHelpers.sameValueSeriesFix(i, series) let realIndex = w.globals.comboCharts ? seriesIndex[i] : i + let translationsIndex = this.yRatio.length > 1 ? realIndex: 0 + this._initSerieVariables(series, i, realIndex) @@ -97,6 +99,7 @@ class Line { series, prevY, lineYPosition, + translationsIndex }) prevY = firstPrevY.prevY if (w.config.stroke.curve === 'monotonCubic' && series[i][0] === null) { @@ -116,6 +119,7 @@ class Line { series: seriesRangeEnd, prevY: prevY2, lineYPosition, + translationsIndex }) prevY2 = firstPrevY2.prevY pY2 = prevY2 @@ -136,6 +140,7 @@ class Line { type, series, realIndex, + translationsIndex, i, x, y, @@ -221,20 +226,25 @@ class Line { ? w.config.stroke.width[realIndex] : w.config.stroke.width + let translationsIndex = 0 if (this.yRatio.length > 1) { - this.yaxisIndex = realIndex + this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex] + translationsIndex = realIndex } this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed + // FIXME: I don't know why we don't need baseLineY for log axes, it just works. + this.isLogY = w.config.yaxis[this.yaxisIndex].logarithmic + // zeroY is the 0 value in y series which can be used in negative charts this.zeroY = w.globals.gridHeight - - this.baseLineY[this.yaxisIndex] - + (this.isLogY ? 0 : this.baseLineY[translationsIndex]) - (this.isReversed ? w.globals.gridHeight : 0) + - (this.isReversed ? this.baseLineY[this.yaxisIndex] * 2 : 0) + (this.isReversed ? this.baseLineY[translationsIndex] * 2 : 0) this.areaBottomY = this.zeroY if ( @@ -288,7 +298,7 @@ class Line { for (let s = 0; s < series[i].length; s++) { if (series[i][s] !== null) { prevX = this.xDivision * s - prevY = this.zeroY - series[i][s] / this.yRatio[this.yaxisIndex] + prevY = this.zeroY - series[i][s] / this.yRatio[realIndex] linePath = graphics.move(prevX, prevY) areaPath = graphics.move(prevX, this.areaBottomY) break @@ -478,6 +488,7 @@ class Line { series, iterations, realIndex, + translationsIndex, i, x, y, @@ -513,8 +524,8 @@ class Line { const getY = (_y, lineYPos) => { return ( lineYPos - - _y / yRatio[this.yaxisIndex] + - (this.isReversed ? _y / yRatio[this.yaxisIndex] : 0) * 2 + _y / yRatio[translationsIndex] + + (this.isReversed ? _y / yRatio[translationsIndex] : 0) * 2 ) } diff --git a/src/charts/Radar.js b/src/charts/Radar.js index 21b93a60b..2791b82be 100644 --- a/src/charts/Radar.js +++ b/src/charts/Radar.js @@ -40,13 +40,14 @@ class Radar { : w.globals.gridWidth this.isLog = w.config.yaxis[0].logarithmic + this.logBase = w.config.yaxis[0].logBase this.coreUtils = new CoreUtils(this.ctx) this.maxValue = this.isLog - ? this.coreUtils.getLogVal(w.globals.maxY, 0) + ? this.coreUtils.getLogVal(this.logBase, w.globals.maxY, 0) : w.globals.maxY this.minValue = this.isLog - ? this.coreUtils.getLogVal(this.w.globals.minY, 0) + ? this.coreUtils.getLogVal(this.logBase, this.w.globals.minY, 0) : w.globals.minY this.polygons = w.config.plotOptions.radar.polygons @@ -122,7 +123,7 @@ class Radar { dv = dv + Math.abs(this.minValue) if (this.isLog) { - dv = this.coreUtils.getLogVal(dv, 0) + dv = this.coreUtils.getLogVal(this.logBase, dv, 0) } this.dataRadiusOfPercent[i][j] = dv / range diff --git a/src/charts/RangeBar.js b/src/charts/RangeBar.js index 5513528bd..8b2478c64 100644 --- a/src/charts/RangeBar.js +++ b/src/charts/RangeBar.js @@ -52,8 +52,10 @@ class RangeBar extends Bar { let barHeight = 0 let barWidth = 0 + let translationsIndex = 0 if (this.yRatio.length > 1) { - this.yaxisIndex = realIndex + this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex][0] + translationsIndex = realIndex } let initPositions = this.barHelpers.initialPositions() @@ -159,7 +161,7 @@ class RangeBar extends Bar { } paths = this.drawRangeColumnPaths({ - indexes: { i, j, realIndex }, + indexes: { i, j, realIndex, translationsIndex }, barWidth, barXPosition, zeroH, @@ -319,7 +321,7 @@ class RangeBar extends Bar { let i = indexes.i let j = indexes.j - const yRatio = this.yRatio[this.yaxisIndex] + const yRatio = this.yRatio[indexes.translationsIndex] let realIndex = indexes.realIndex const range = this.getRangeValue(realIndex, j) diff --git a/src/charts/common/bar/Helpers.js b/src/charts/common/bar/Helpers.js index 34d2fc1de..dbf5ced4f 100644 --- a/src/charts/common/bar/Helpers.js +++ b/src/charts/common/bar/Helpers.js @@ -134,8 +134,7 @@ export default class Helpers { ? this.barCtx.baseLineY[this.barCtx.yaxisIndex] * 2 : 0) - x = - w.globals.padHorizontal + + x = w.globals.padHorizontal + (xDivision - barWidth * this.barCtx.seriesLen) / 2 } diff --git a/src/charts/common/line/Helpers.js b/src/charts/common/line/Helpers.js index 9021513f1..563a9709c 100644 --- a/src/charts/common/line/Helpers.js +++ b/src/charts/common/line/Helpers.js @@ -102,7 +102,7 @@ export default class Helpers { } } - determineFirstPrevY({ i, series, prevY, lineYPosition }) { + determineFirstPrevY({ i, series, prevY, lineYPosition, translationsIndex }) { let w = this.w let stackSeries = (w.config.chart.stacked && !w.globals.comboCharts) || @@ -125,11 +125,9 @@ export default class Helpers { } prevY = lineYPosition - - series[i][0] / this.lineCtx.yRatio[this.lineCtx.yaxisIndex] + - (this.lineCtx.isReversed - ? series[i][0] / this.lineCtx.yRatio[this.lineCtx.yaxisIndex] - : 0) * - 2 + series[i][0] / this.lineCtx.yRatio[translationsIndex] + + (this.lineCtx.isReversed + ? series[i][0] / this.lineCtx.yRatio[translationsIndex] : 0) * 2 } else { // the first value in the current series is null if (stackSeries && i > 0 && typeof series[i][0] === 'undefined') { diff --git a/src/modules/CoreUtils.js b/src/modules/CoreUtils.js index bd89aa474..d46633c6d 100644 --- a/src/modules/CoreUtils.js +++ b/src/modules/CoreUtils.js @@ -324,10 +324,11 @@ class CoreUtils { const w = this.w w.globals.seriesLog = series.map((s, i) => { - if (w.config.yaxis[i] && w.config.yaxis[i].logarithmic) { + let yAxisIndex = w.globals.seriesYAxisReverseMap[i] + if (w.config.yaxis[yAxisIndex] && w.config.yaxis[yAxisIndex].logarithmic) { return s.map((d) => { if (d === null) return null - return this.getLogVal(w.config.yaxis[i].logBase, d, i) + return this.getLogVal(w.config.yaxis[yAxisIndex].logBase, d, i) }) } else { return s @@ -339,19 +340,19 @@ class CoreUtils { getBaseLog(base, value) { return Math.log(value) / Math.log(base) } - getLogVal(b, d, yIndex) { - if (d === 0) { - return 0 + getLogVal(b, d, seriesIndex) { + if (d <= 0) { + return 0 // Should be Number.NEGATIVE_INFINITY } const w = this.w const min_log_val = - w.globals.minYArr[yIndex] === 0 + w.globals.minYArr[seriesIndex] === 0 ? -1 // make sure we dont calculate log of 0 - : this.getBaseLog(b, w.globals.minYArr[yIndex]) + : this.getBaseLog(b, w.globals.minYArr[seriesIndex]) const max_log_val = - w.globals.maxYArr[yIndex] === 0 + w.globals.maxYArr[seriesIndex] === 0 ? 0 // make sure we dont calculate log of 0 - : this.getBaseLog(b, w.globals.maxYArr[yIndex]) + : this.getBaseLog(b, w.globals.maxYArr[seriesIndex]) const number_of_height_levels = max_log_val - min_log_val if (d < 1) return d / number_of_height_levels const log_height_value = this.getBaseLog(b, d) - min_log_val @@ -365,7 +366,8 @@ class CoreUtils { gl.yLogRatio = yRatio.slice() gl.logYRange = gl.yRange.map((yRange, i) => { - if (w.config.yaxis[i] && this.w.config.yaxis[i].logarithmic) { + let yAxisIndex = w.globals.seriesYAxisReverseMap[i] + if (w.config.yaxis[yAxisIndex] && this.w.config.yaxis[yAxisIndex].logarithmic) { let maxY = -Number.MAX_VALUE let minY = Number.MIN_VALUE let range = 1 diff --git a/src/modules/Scales.js b/src/modules/Scales.js index f131988a2..d36a975c4 100644 --- a/src/modules/Scales.js +++ b/src/modules/Scales.js @@ -429,7 +429,8 @@ export default class Scales { const logs = [] - const logMax = Math.ceil(Math.log(yMax) / Math.log(base) + 1) // Get powers of base for our max and min + // Get powers of base for our max and min + const logMax = Math.ceil(Math.log(yMax) / Math.log(base) + 1) const logMin = Math.floor(Math.log(yMin) / Math.log(base)) for (let i = logMin; i < logMax; i++) { @@ -571,9 +572,55 @@ export default class Scales { const minYArr = gl.minYArr const maxYArr = gl.maxYArr + // The current config method to map multiple series to a y axis is to + // include one yaxis config per series but set each yaxis seriesName to the + // same series name. This relies on indexing equivalence to map series to + // an axis: series[n] => yaxis[n]. This needs to be retained for compatibility. + // But we introduce an alternative that explicitly configures yaxis elements + // with the series that will be referenced to them (seriesName: []). This + // only requires including the yaxis elements that will be seen on the chart. + // Old way: + // ya: s + // 0: 0 + // 1: 1 + // 2: 1 + // 3: 1 + // 4: 1 + // Axes 0..4 are all scaled and all will be rendered unless the axes are + // show: false. If the chart is stacked, it's assumed that series 1..4 are + // the contributing series. This is not particularly intuitive. + // New way: + // ya: s + // 0: [0] + // 1: [1,2,3,4] + // If the chart is stacked, it can be assumed that any axis with multiple + // series is stacked. + // + // If this is an old chart and we are being backward compatible, it will be + // expected that each series is associated with it's corresponding yaxis + // through their indices, one-to-one. + // If yaxis.seriesName matches series.name, we have indices yi and si. + // A name match where yi != si is interpretted as yaxis[yi] and yaxis[si] + // will both be scaled to fit the combined series[si] and series[yi]. + // Consider series named: S0,S1,S2 and yaxes A0,A1,A2. + // + // Example 1: A0 and A1 scaled the same. + // A0.seriesName: S0 + // A1.seriesName: S0 + // A2.seriesName: S2 + // Then A1 <-> A0 + // + // Example 2: A0, A1 and A2 all scaled the same. + // A0.seriesName: S2 + // A1.seriesName: S0 + // A2.seriesName: S1 + // A0 <-> A2, A1 <-> A0, A2 <-> A1 --->>> A0 <-> A1 <-> A2 + let axisSeriesMap = [] let seriesYAxisReverseMap = [] let unassignedSeriesIndices = [] + let assumeSeriesNameArrays = cnf.yaxis.length !== cnf.series.length + cnf.series.forEach((s, i) => { unassignedSeriesIndices.push(i) seriesYAxisReverseMap.push(null) @@ -600,16 +647,13 @@ export default class Scales { seriesNames.forEach((name) => { cnf.series.forEach((s, si) => { if (s.name === name) { - assigned = true - axisSeriesMap[yi].push(si) - let remove - if (yi === si) { - seriesYAxisReverseMap[si] = yi - remove = unassignedSeriesIndices.indexOf(si) + if (yi === si || assumeSeriesNameArrays) { + axisSeriesMap[yi].push(si) } else { - seriesYAxisReverseMap[yi] = yi - remove = unassignedSeriesIndices.indexOf(yi) + axisSeriesMap[si].push(yi) } + assigned = true + let remove = unassignedSeriesIndices.indexOf(si) if (remove !== -1) { unassignedSeriesIndices.splice(remove, 1) } @@ -621,6 +665,11 @@ export default class Scales { unassignedYAxisIndices.push(yi) } }) + axisSeriesMap.forEach((yaxe, yi) => { + yaxe.forEach((si) => { + seriesYAxisReverseMap[si] = yi + }) + }) // All series referenced directly by yaxes have been assigned to those axes. // Any series so far unassigned will be assigned to any yaxes that have yet // to reference series directly, one-for-one in order of appearance, with @@ -657,73 +706,6 @@ export default class Scales { const cnf = this.w.config const gl = this.w.globals - // The current config method to map multiple series to a y axis is to - // include one yaxis config per series but set each yaxis seriesName to the - // same series name. This relies on indexing equivalence to map series to - // an axis: series[n] => yaxis[n]. This needs to be retained for compatibility. - // But we introduce an alternative that explicitly configures yaxis elements - // with the series that will be referenced to them (seriesName: []). This - // only requires including the yaxis elements that will be seen on the chart. - // Old way: - // ya: s - // 0: 0 - // 1: 1 - // 2: 1 - // 3: 1 - // 4: 1 - // Axes 0..4 are all scaled and all will be rendered unless the axes are - // show: false. If the chart is stacked, it's assumed that series 1..4 are - // the contributing series. This is not particularly intuitive. - // New way: - // ya: s - // 0: [0] - // 1: [1,2,3,4] - // If the chart is stacked, it can be assumed that any axis with multiple - // series is stacked. - // - // If this is an old chart and we are being backward compatible, it will be - // expected that each series is associated with it's corresponding yaxis - // through their indices, one-to-one. - // If yaxis.seriesName matches series.name, we have indices yi and si. - // A name match where yi != si is interpretted as yaxis[yi] and yaxis[si] - // will both be scaled to fit the combined series[si] and series[yi]. - // Consider series named: S0,S1,S2 and yaxes A0,A1,A2. - // - // Example 1: A0 and A1 scaled the same. - // A0.seriesName: S0 - // A1.seriesName: S0 - // A2.seriesName: S2 - // Then A1 <-> A0 - // - // Example 2: A0, A1 and A2 all scaled the same. - // A0.seriesName: S2 - // A1.seriesName: S0 - // A2.seriesName: S1 - // A0 <-> A2, A1 <-> A0, A2 <-> A1 --->>> A0 <-> A1 <-> A2 - - // First things first, convert the old to the new. - - // Consolidate series indices into axes - if (axisSeriesMap.length === cnf.series.length) { - axisSeriesMap.forEach((axisSeries, ai) => { - let si = axisSeries[0] - if (si !== ai) { - axisSeriesMap[si].push(ai) - axisSeriesMap[ai].splice(0,1) - } - }) - // Now duplicate axisSeriesMap elements that need to be the same. - axisSeriesMap.forEach((axisSeries, ai) => { - if (!axisSeries.length) { - axisSeriesMap.forEach((as, i) => { - if (as.indexOf(ai) > -1) { - axisSeriesMap[ai] = axisSeriesMap[i].map((x) => x) - } - }) - } - }) - } - // Compute min..max for each yaxis // axisSeriesMap.forEach((axisSeries, ai) => { diff --git a/src/modules/ZoomPanSelection.js b/src/modules/ZoomPanSelection.js index e372264a9..10ded9e44 100644 --- a/src/modules/ZoomPanSelection.js +++ b/src/modules/ZoomPanSelection.js @@ -572,11 +572,14 @@ export default class ZoomPanSelection extends Toolbar { let yLowestValue = [] w.config.yaxis.forEach((yaxe, index) => { + // We can use the index of any series referenced by the Yaxis + // because they will all return the same value. + let seriesIndex = w.globals.seriesYAxisMap[index][0] yHighestValue.push( - w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[index] * me.startY + w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[seriesIndex] * me.startY ) yLowestValue.push( - w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[index] * me.endY + w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[seriesIndex] * me.endY ) }) diff --git a/src/modules/annotations/Helpers.js b/src/modules/annotations/Helpers.js index c6999ec05..569605f00 100644 --- a/src/modules/annotations/Helpers.js +++ b/src/modules/annotations/Helpers.js @@ -148,6 +148,7 @@ export default class Helpers { getY1Y2(type, anno) { let y = type === 'y1' ? anno.y : anno.y2 let yP + let clipped = null const w = this.w if (this.annoCtx.invertAxis) { @@ -180,14 +181,27 @@ export default class Helpers { } } else { let yPos + // We can use the index of any series referenced by the Yaxis + // because they will all return the same value. + let seriesIndex = w.globals.seriesYAxisMap[anno.yAxisIndex][0] if (w.config.yaxis[anno.yAxisIndex].logarithmic) { const coreUtils = new CoreUtils(this.annoCtx.ctx) - y = coreUtils.getLogVal(y, anno.yAxisIndex) - yPos = y / w.globals.yLogRatio[anno.yAxisIndex] + y = coreUtils.getLogVal( + w.config.yaxis[anno.yAxisIndex].logBase, + y, + seriesIndex) + yPos = y / w.globals.yLogRatio[seriesIndex] } else { yPos = - (y - w.globals.minYArr[anno.yAxisIndex]) / - (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + (y - w.globals.minYArr[seriesIndex]) / + (w.globals.yRange[seriesIndex] / w.globals.gridHeight) + } + if (yPos > w.globals.gridHeight) { + yPos = w.globals.gridHeight + clipped = 1 // Clipped at top + } else if (yPos < 0) { + yPos = 0 + clipped = 0 // Clipped at bottom } yP = w.globals.gridHeight - yPos @@ -201,6 +215,9 @@ export default class Helpers { w.config.yaxis[anno.yAxisIndex].reversed ) { yP = yPos + if (clipped) { + clipped = 1 - clipped // Switch top and bottom + } } } @@ -208,7 +225,7 @@ export default class Helpers { yP = parseFloat(y) } - return yP + return {'yP': yP, 'clipped': clipped} } getX1X2(type, anno) { diff --git a/src/modules/annotations/PointsAnnotations.js b/src/modules/annotations/PointsAnnotations.js index 632db6fe1..11a8121ef 100644 --- a/src/modules/annotations/PointsAnnotations.js +++ b/src/modules/annotations/PointsAnnotations.js @@ -12,101 +12,105 @@ export default class PointAnnotations { const w = this.w let x = this.helpers.getX1X2('x1', anno) - let y = this.helpers.getY1Y2('y1', anno) + let result = this.helpers.getY1Y2('y1', anno) + let y = result.yP + let clipY1 = result.clipped if (!Utils.isNumber(x)) return - let optsPoints = { - pSize: anno.marker.size, - pointStrokeWidth: anno.marker.strokeWidth, - pointFillColor: anno.marker.fillColor, - pointStrokeColor: anno.marker.strokeColor, - shape: anno.marker.shape, - pRadius: anno.marker.radius, - class: `apexcharts-point-annotation-marker ${anno.marker.cssClass} ${ - anno.id ? anno.id : '' - }`, - } - - let point = this.annoCtx.graphics.drawMarker( - x + anno.marker.offsetX, - y + anno.marker.offsetY, - optsPoints - ) - - parent.appendChild(point.node) - - const text = anno.label.text ? anno.label.text : '' - - let elText = this.annoCtx.graphics.drawText({ - x: x + anno.label.offsetX, - y: - y + - anno.label.offsetY - - anno.marker.size - - parseFloat(anno.label.style.fontSize) / 1.6, - text, - textAnchor: anno.label.textAnchor, - fontSize: anno.label.style.fontSize, - fontFamily: anno.label.style.fontFamily, - fontWeight: anno.label.style.fontWeight, - foreColor: anno.label.style.color, - cssClass: `apexcharts-point-annotation-label ${ - anno.label.style.cssClass - } ${anno.id ? anno.id : ''}`, - }) - - elText.attr({ - rel: index, - }) - - parent.appendChild(elText.node) - - // TODO: deprecate this as we will use custom - if (anno.customSVG.SVG) { - let g = this.annoCtx.graphics.group({ - class: - 'apexcharts-point-annotations-custom-svg ' + anno.customSVG.cssClass, - }) + if (clipY1 === null) { + let optsPoints = { + pSize: anno.marker.size, + pointStrokeWidth: anno.marker.strokeWidth, + pointFillColor: anno.marker.fillColor, + pointStrokeColor: anno.marker.strokeColor, + shape: anno.marker.shape, + pRadius: anno.marker.radius, + class: `apexcharts-point-annotation-marker ${anno.marker.cssClass} ${ + anno.id ? anno.id : '' + }`, + } + + let point = this.annoCtx.graphics.drawMarker( + x + anno.marker.offsetX, + y + anno.marker.offsetY, + optsPoints + ) - g.attr({ - transform: `translate(${x + anno.customSVG.offsetX}, ${ - y + anno.customSVG.offsetY - })`, + parent.appendChild(point.node) + + const text = anno.label.text ? anno.label.text : '' + + let elText = this.annoCtx.graphics.drawText({ + x: x + anno.label.offsetX, + y: + y + + anno.label.offsetY - + anno.marker.size - + parseFloat(anno.label.style.fontSize) / 1.6, + text, + textAnchor: anno.label.textAnchor, + fontSize: anno.label.style.fontSize, + fontFamily: anno.label.style.fontFamily, + fontWeight: anno.label.style.fontWeight, + foreColor: anno.label.style.color, + cssClass: `apexcharts-point-annotation-label ${ + anno.label.style.cssClass + } ${anno.id ? anno.id : ''}`, }) - g.node.innerHTML = anno.customSVG.SVG - parent.appendChild(g.node) - } - - if (anno.image.path) { - let imgWidth = anno.image.width ? anno.image.width : 20 - let imgHeight = anno.image.height ? anno.image.height : 20 - - point = this.annoCtx.addImage({ - x: x + anno.image.offsetX - imgWidth / 2, - y: y + anno.image.offsetY - imgHeight / 2, - width: imgWidth, - height: imgHeight, - path: anno.image.path, - appendTo: '.apexcharts-point-annotations', + elText.attr({ + rel: index, }) - } - if (anno.mouseEnter) { - point.node.addEventListener( - 'mouseenter', - anno.mouseEnter.bind(this, anno) - ) - } - if (anno.mouseLeave) { - point.node.addEventListener( - 'mouseleave', - anno.mouseLeave.bind(this, anno) - ) - } - if (anno.click) { - point.node.addEventListener('click', anno.click.bind(this, anno)) + parent.appendChild(elText.node) + + // TODO: deprecate this as we will use custom + if (anno.customSVG.SVG) { + let g = this.annoCtx.graphics.group({ + class: + 'apexcharts-point-annotations-custom-svg ' + anno.customSVG.cssClass, + }) + + g.attr({ + transform: `translate(${x + anno.customSVG.offsetX}, ${ + y + anno.customSVG.offsetY + })`, + }) + + g.node.innerHTML = anno.customSVG.SVG + parent.appendChild(g.node) + } + + if (anno.image.path) { + let imgWidth = anno.image.width ? anno.image.width : 20 + let imgHeight = anno.image.height ? anno.image.height : 20 + + point = this.annoCtx.addImage({ + x: x + anno.image.offsetX - imgWidth / 2, + y: y + anno.image.offsetY - imgHeight / 2, + width: imgWidth, + height: imgHeight, + path: anno.image.path, + appendTo: '.apexcharts-point-annotations', + }) + } + + if (anno.mouseEnter) { + point.node.addEventListener( + 'mouseenter', + anno.mouseEnter.bind(this, anno) + ) + } + if (anno.mouseLeave) { + point.node.addEventListener( + 'mouseleave', + anno.mouseLeave.bind(this, anno) + ) + } + if (anno.click) { + point.node.addEventListener('click', anno.click.bind(this, anno)) + } } } diff --git a/src/modules/annotations/YAxisAnnotations.js b/src/modules/annotations/YAxisAnnotations.js index c9be54206..8f344d8de 100644 --- a/src/modules/annotations/YAxisAnnotations.js +++ b/src/modules/annotations/YAxisAnnotations.js @@ -1,4 +1,5 @@ import Helpers from './Helpers' +import AxesUtils from '../axes/AxesUtils' export default class YAnnotations { constructor(annoCtx) { @@ -6,6 +7,8 @@ export default class YAnnotations { this.annoCtx = annoCtx this.helpers = new Helpers(this.annoCtx) + this.axesUtils = new AxesUtils(this.annoCtx) + } addYaxisAnnotation(anno, parent, index) { @@ -13,27 +16,36 @@ export default class YAnnotations { let strokeDashArray = anno.strokeDashArray - let y1 = this.helpers.getY1Y2('y1', anno) + let result = this.helpers.getY1Y2('y1', anno) + let y1 = result.yP + let clipY1 = result.clipped let y2 + let clipY2 + let drawn = false const text = anno.label.text if (anno.y2 === null || typeof anno.y2 === 'undefined') { - let line = this.annoCtx.graphics.drawLine( - 0 + anno.offsetX, // x1 - y1 + anno.offsetY, // y1 - this._getYAxisAnnotationWidth(anno), // x2 - y1 + anno.offsetY, // y2 - anno.borderColor, // lineColor - strokeDashArray, // dashArray - anno.borderWidth - ) - parent.appendChild(line.node) - if (anno.id) { - line.node.classList.add(anno.id) + if (clipY1 === null) { + drawn = true + let line = this.annoCtx.graphics.drawLine( + 0 + anno.offsetX, // x1 + y1 + anno.offsetY, // y1 + this._getYAxisAnnotationWidth(anno), // x2 + y1 + anno.offsetY, // y2 + anno.borderColor, // lineColor + strokeDashArray, // dashArray + anno.borderWidth + ) + parent.appendChild(line.node) + if (anno.id) { + line.node.classList.add(anno.id) + } } } else { - y2 = this.helpers.getY1Y2('y2', anno) + result = this.helpers.getY1Y2('y2', anno) + y2 = result.yP + clipY2 = result.clipped if (y2 > y1) { let temp = y1 @@ -41,52 +53,57 @@ export default class YAnnotations { y2 = temp } - let rect = this.annoCtx.graphics.drawRect( - 0 + anno.offsetX, // x1 - y2 + anno.offsetY, // y1 - this._getYAxisAnnotationWidth(anno), // x2 - y1 - y2, // y2 - 0, // radius - anno.fillColor, // color - anno.opacity, // opacity, - 1, // strokeWidth - anno.borderColor, // strokeColor - strokeDashArray // stokeDashArray - ) - rect.node.classList.add('apexcharts-annotation-rect') - rect.attr('clip-path', `url(#gridRectMask${w.globals.cuid})`) - - parent.appendChild(rect.node) - if (anno.id) { - rect.node.classList.add(anno.id) + if (clipY1 === null || clipY2 === null) { + drawn = true + let rect = this.annoCtx.graphics.drawRect( + 0 + anno.offsetX, // x1 + y2 + anno.offsetY, // y1 + this._getYAxisAnnotationWidth(anno), // x2 + y1 - y2, // y2 + 0, // radius + anno.fillColor, // color + anno.opacity, // opacity, + 1, // strokeWidth + anno.borderColor, // strokeColor + strokeDashArray // stokeDashArray + ) + rect.node.classList.add('apexcharts-annotation-rect') + rect.attr('clip-path', `url(#gridRectMask${w.globals.cuid})`) + + parent.appendChild(rect.node) + if (anno.id) { + rect.node.classList.add(anno.id) + } } } - let textX = - anno.label.position === 'right' - ? w.globals.gridWidth - : anno.label.position === 'center' - ? w.globals.gridWidth / 2 - : 0 - - let elText = this.annoCtx.graphics.drawText({ - x: textX + anno.label.offsetX, - y: (y2 != null ? y2 : y1) + anno.label.offsetY - 3, - text, - textAnchor: anno.label.textAnchor, - fontSize: anno.label.style.fontSize, - fontFamily: anno.label.style.fontFamily, - fontWeight: anno.label.style.fontWeight, - foreColor: anno.label.style.color, - cssClass: `apexcharts-yaxis-annotation-label ${ - anno.label.style.cssClass - } ${anno.id ? anno.id : ''}` - }) - - elText.attr({ - rel: index - }) - - parent.appendChild(elText.node) + if (drawn) { + let textX = + anno.label.position === 'right' + ? w.globals.gridWidth + : anno.label.position === 'center' + ? w.globals.gridWidth / 2 + : 0 + + let elText = this.annoCtx.graphics.drawText({ + x: textX + anno.label.offsetX, + y: (y2 != null ? y2 : y1) + anno.label.offsetY - 3, + text, + textAnchor: anno.label.textAnchor, + fontSize: anno.label.style.fontSize, + fontFamily: anno.label.style.fontFamily, + fontWeight: anno.label.style.fontWeight, + foreColor: anno.label.style.color, + cssClass: `apexcharts-yaxis-annotation-label ${ + anno.label.style.cssClass + } ${anno.id ? anno.id : ''}` + }) + + elText.attr({ + rel: index + }) + + parent.appendChild(elText.node) + } } _getYAxisAnnotationWidth(anno) { @@ -108,8 +125,11 @@ export default class YAnnotations { class: 'apexcharts-yaxis-annotations' }) - w.config.annotations.yaxis.map((anno, index) => { - this.addYaxisAnnotation(anno, elg.node, index) + w.config.annotations.yaxis.forEach((anno, index) => { + let seriesIndex = w.globals.seriesYAxisMap[anno.yAxisIndex][0] + if (!this.axesUtils.isYAxisHidden(anno.yAxisIndex)) { + this.addYaxisAnnotation(anno, elg.node, index) + } }) return elg diff --git a/src/modules/axes/AxesUtils.js b/src/modules/axes/AxesUtils.js index 7dbed05ac..ff7ba8a01 100644 --- a/src/modules/axes/AxesUtils.js +++ b/src/modules/axes/AxesUtils.js @@ -185,10 +185,10 @@ export default class AxesUtils { }) return ( + allCollapsed || !w.config.yaxis[index].show || (!w.config.yaxis[index].showForNullSeries && - coreUtils.isSeriesNull(index) && - allCollapsed) + coreUtils.isSeriesNull(index)) ) } diff --git a/src/modules/tooltip/AxesTooltip.js b/src/modules/tooltip/AxesTooltip.js index eb5a88397..eac570ad3 100644 --- a/src/modules/tooltip/AxesTooltip.js +++ b/src/modules/tooltip/AxesTooltip.js @@ -168,10 +168,12 @@ class AxesTooltip { const elGrid = ttCtx.getElGrid() const seriesBound = elGrid.getBoundingClientRect() - const hoverY = (clientY - seriesBound.top) * xyRatios.yRatio[index] - const height = w.globals.maxYArr[index] - w.globals.minYArr[index] - - const val = w.globals.minYArr[index] + (height - hoverY) + // We can use the index of any series referenced by the Yaxis + // because they will all return the same value. + let seriesIndex = w.globals.seriesYAxisMap[anno.yAxisIndex][0] + const hoverY = (clientY - seriesBound.top) * xyRatios.yRatio[seriesIndex] + const height = w.globals.maxYArr[seriesIndex] - w.globals.minYArr[seriesIndex] + const val = w.globals.minYArr[seriesIndex] + (height - hoverY) ttCtx.tooltipPosition.moveYCrosshairs(clientY - seriesBound.top) ttCtx.yaxisTooltipText[index].innerHTML = lbFormatter(val)