initial commit
This commit is contained in:
@@ -0,0 +1,638 @@
|
||||
import Fill from '../../../modules/Fill'
|
||||
import Graphics from '../../../modules/Graphics'
|
||||
import Series from '../../../modules/Series'
|
||||
|
||||
export default class Helpers {
|
||||
constructor(barCtx) {
|
||||
this.w = barCtx.w
|
||||
this.barCtx = barCtx
|
||||
}
|
||||
|
||||
initVariables(series) {
|
||||
const w = this.w
|
||||
this.barCtx.series = series
|
||||
this.barCtx.totalItems = 0
|
||||
this.barCtx.seriesLen = 0
|
||||
this.barCtx.visibleI = -1 // visible Series
|
||||
this.barCtx.visibleItems = 1 // number of visible bars after user zoomed in/out
|
||||
|
||||
for (let sl = 0; sl < series.length; sl++) {
|
||||
if (series[sl].length > 0) {
|
||||
this.barCtx.seriesLen = this.barCtx.seriesLen + 1
|
||||
this.barCtx.totalItems += series[sl].length
|
||||
}
|
||||
if (w.globals.isXNumeric) {
|
||||
// get max visible items
|
||||
for (let j = 0; j < series[sl].length; j++) {
|
||||
if (
|
||||
w.globals.seriesX[sl][j] > w.globals.minX &&
|
||||
w.globals.seriesX[sl][j] < w.globals.maxX
|
||||
) {
|
||||
this.barCtx.visibleItems++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.barCtx.visibleItems = w.globals.dataPoints
|
||||
}
|
||||
}
|
||||
|
||||
if (this.barCtx.seriesLen === 0) {
|
||||
// A small adjustment when combo charts are used
|
||||
this.barCtx.seriesLen = 1
|
||||
}
|
||||
this.barCtx.zeroSerieses = []
|
||||
this.barCtx.radiusOnSeriesNumber = series.length - 1 // which series to draw ending shape on
|
||||
|
||||
if (!w.globals.comboCharts) {
|
||||
this.checkZeroSeries({ series })
|
||||
}
|
||||
}
|
||||
|
||||
initialPositions() {
|
||||
let w = this.w
|
||||
let x, y, yDivision, xDivision, barHeight, barWidth, zeroH, zeroW
|
||||
|
||||
let dataPoints = w.globals.dataPoints
|
||||
if (this.barCtx.isRangeBar) {
|
||||
// timeline rangebar chart
|
||||
dataPoints = w.globals.labels.length
|
||||
}
|
||||
|
||||
let seriesLen = this.barCtx.seriesLen
|
||||
if (w.config.plotOptions.bar.rangeBarGroupRows) {
|
||||
seriesLen = 1
|
||||
}
|
||||
|
||||
if (this.barCtx.isHorizontal) {
|
||||
// height divided into equal parts
|
||||
yDivision = w.globals.gridHeight / dataPoints
|
||||
barHeight = yDivision / seriesLen
|
||||
|
||||
if (w.globals.isXNumeric) {
|
||||
yDivision = w.globals.gridHeight / this.barCtx.totalItems
|
||||
barHeight = yDivision / this.barCtx.seriesLen
|
||||
}
|
||||
|
||||
barHeight =
|
||||
(barHeight * parseInt(this.barCtx.barOptions.barHeight, 10)) / 100
|
||||
|
||||
zeroW =
|
||||
this.barCtx.baseLineInvertedY +
|
||||
w.globals.padHorizontal +
|
||||
(this.barCtx.isReversed ? w.globals.gridWidth : 0) -
|
||||
(this.barCtx.isReversed ? this.barCtx.baseLineInvertedY * 2 : 0)
|
||||
|
||||
y = (yDivision - barHeight * this.barCtx.seriesLen) / 2
|
||||
} else {
|
||||
// width divided into equal parts
|
||||
xDivision = w.globals.gridWidth / this.barCtx.visibleItems
|
||||
if (w.config.xaxis.convertedCatToNumeric) {
|
||||
xDivision = w.globals.gridWidth / w.globals.dataPoints
|
||||
}
|
||||
barWidth =
|
||||
((xDivision / this.barCtx.seriesLen) *
|
||||
parseInt(this.barCtx.barOptions.columnWidth, 10)) /
|
||||
100
|
||||
|
||||
if (w.globals.isXNumeric) {
|
||||
// max barwidth should be equal to minXDiff to avoid overlap
|
||||
let xRatio = this.barCtx.xRatio
|
||||
if (w.config.xaxis.convertedCatToNumeric) {
|
||||
xRatio = this.barCtx.initialXRatio
|
||||
}
|
||||
if (
|
||||
w.globals.minXDiff &&
|
||||
w.globals.minXDiff !== 0.5 &&
|
||||
w.globals.minXDiff / xRatio > 0
|
||||
) {
|
||||
xDivision = w.globals.minXDiff / xRatio
|
||||
}
|
||||
|
||||
barWidth =
|
||||
((xDivision / this.barCtx.seriesLen) *
|
||||
parseInt(this.barCtx.barOptions.columnWidth, 10)) /
|
||||
100
|
||||
|
||||
if (barWidth < 1) {
|
||||
barWidth = 1
|
||||
}
|
||||
}
|
||||
|
||||
zeroH =
|
||||
w.globals.gridHeight -
|
||||
this.barCtx.baseLineY[this.barCtx.yaxisIndex] -
|
||||
(this.barCtx.isReversed ? w.globals.gridHeight : 0) +
|
||||
(this.barCtx.isReversed
|
||||
? this.barCtx.baseLineY[this.barCtx.yaxisIndex] * 2
|
||||
: 0)
|
||||
|
||||
x =
|
||||
w.globals.padHorizontal +
|
||||
(xDivision - barWidth * this.barCtx.seriesLen) / 2
|
||||
}
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
yDivision,
|
||||
xDivision,
|
||||
barHeight,
|
||||
barWidth,
|
||||
zeroH,
|
||||
zeroW
|
||||
}
|
||||
}
|
||||
|
||||
getPathFillColor(series, i, j, realIndex) {
|
||||
const w = this.w
|
||||
let fill = new Fill(this.barCtx.ctx)
|
||||
|
||||
let fillColor = null
|
||||
let seriesNumber = this.barCtx.barOptions.distributed ? j : i
|
||||
|
||||
if (this.barCtx.barOptions.colors.ranges.length > 0) {
|
||||
const colorRange = this.barCtx.barOptions.colors.ranges
|
||||
colorRange.map((range) => {
|
||||
if (series[i][j] >= range.from && series[i][j] <= range.to) {
|
||||
fillColor = range.color
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (w.config.series[i].data[j] && w.config.series[i].data[j].fillColor) {
|
||||
fillColor = w.config.series[i].data[j].fillColor
|
||||
}
|
||||
|
||||
let pathFill = fill.fillPath({
|
||||
seriesNumber: this.barCtx.barOptions.distributed
|
||||
? seriesNumber
|
||||
: realIndex,
|
||||
dataPointIndex: j,
|
||||
color: fillColor,
|
||||
value: series[i][j]
|
||||
})
|
||||
|
||||
return pathFill
|
||||
}
|
||||
|
||||
getStrokeWidth(i, j, realIndex) {
|
||||
let strokeWidth = 0
|
||||
const w = this.w
|
||||
|
||||
if (
|
||||
typeof this.barCtx.series[i][j] === 'undefined' ||
|
||||
this.barCtx.series[i][j] === null
|
||||
) {
|
||||
this.barCtx.isNullValue = true
|
||||
} else {
|
||||
this.barCtx.isNullValue = false
|
||||
}
|
||||
if (w.config.stroke.show) {
|
||||
if (!this.barCtx.isNullValue) {
|
||||
strokeWidth = Array.isArray(this.barCtx.strokeWidth)
|
||||
? this.barCtx.strokeWidth[realIndex]
|
||||
: this.barCtx.strokeWidth
|
||||
}
|
||||
}
|
||||
return strokeWidth
|
||||
}
|
||||
|
||||
barBackground({ j, i, x1, x2, y1, y2, elSeries }) {
|
||||
const w = this.w
|
||||
const graphics = new Graphics(this.barCtx.ctx)
|
||||
|
||||
const sr = new Series(this.barCtx.ctx)
|
||||
let activeSeriesIndex = sr.getActiveConfigSeriesIndex()
|
||||
|
||||
if (
|
||||
this.barCtx.barOptions.colors.backgroundBarColors.length > 0 &&
|
||||
activeSeriesIndex === i
|
||||
) {
|
||||
if (j >= this.barCtx.barOptions.colors.backgroundBarColors.length) {
|
||||
j %= this.barCtx.barOptions.colors.backgroundBarColors.length
|
||||
}
|
||||
|
||||
let bcolor = this.barCtx.barOptions.colors.backgroundBarColors[j]
|
||||
let rect = graphics.drawRect(
|
||||
typeof x1 !== 'undefined' ? x1 : 0,
|
||||
typeof y1 !== 'undefined' ? y1 : 0,
|
||||
typeof x2 !== 'undefined' ? x2 : w.globals.gridWidth,
|
||||
typeof y2 !== 'undefined' ? y2 : w.globals.gridHeight,
|
||||
this.barCtx.barOptions.colors.backgroundBarRadius,
|
||||
bcolor,
|
||||
this.barCtx.barOptions.colors.backgroundBarOpacity
|
||||
)
|
||||
elSeries.add(rect)
|
||||
rect.node.classList.add('apexcharts-backgroundBar')
|
||||
}
|
||||
}
|
||||
|
||||
getColumnPaths({
|
||||
barWidth,
|
||||
barXPosition,
|
||||
yRatio,
|
||||
y1,
|
||||
y2,
|
||||
strokeWidth,
|
||||
series,
|
||||
realIndex,
|
||||
i,
|
||||
j,
|
||||
w
|
||||
}) {
|
||||
const graphics = new Graphics(this.barCtx.ctx)
|
||||
strokeWidth = Array.isArray(strokeWidth)
|
||||
? strokeWidth[realIndex]
|
||||
: strokeWidth
|
||||
if (!strokeWidth) strokeWidth = 0
|
||||
|
||||
let shapeOpts = {
|
||||
barWidth,
|
||||
strokeWidth,
|
||||
yRatio,
|
||||
barXPosition,
|
||||
y1,
|
||||
y2
|
||||
}
|
||||
let newPath = this.getRoundedBars(w, shapeOpts, series, i, j)
|
||||
|
||||
const x1 = barXPosition
|
||||
const x2 = barXPosition + barWidth
|
||||
|
||||
let pathTo = graphics.move(x1, y1)
|
||||
let pathFrom = graphics.move(x1, y1)
|
||||
|
||||
const sl = graphics.line(x2 - strokeWidth, y1)
|
||||
if (w.globals.previousPaths.length > 0) {
|
||||
pathFrom = this.barCtx.getPreviousPath(realIndex, j, false)
|
||||
}
|
||||
|
||||
pathTo =
|
||||
pathTo +
|
||||
graphics.line(x1, newPath.y2) +
|
||||
newPath.pathWithRadius +
|
||||
graphics.line(x2 - strokeWidth, newPath.y2) +
|
||||
sl +
|
||||
sl +
|
||||
'z'
|
||||
|
||||
// the lines in pathFrom are repeated to equal it to the points of pathTo
|
||||
// this is to avoid weird animation (bug in svg.js)
|
||||
pathFrom =
|
||||
pathFrom +
|
||||
graphics.line(x1, y1) +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
graphics.line(x1, y1)
|
||||
|
||||
if (w.config.chart.stacked) {
|
||||
this.barCtx.yArrj.push(newPath.y2)
|
||||
this.barCtx.yArrjF.push(Math.abs(y1 - newPath.y2))
|
||||
this.barCtx.yArrjVal.push(this.barCtx.series[i][j])
|
||||
}
|
||||
|
||||
return {
|
||||
pathTo,
|
||||
pathFrom
|
||||
}
|
||||
}
|
||||
|
||||
getBarpaths({
|
||||
barYPosition,
|
||||
barHeight,
|
||||
x1,
|
||||
x2,
|
||||
strokeWidth,
|
||||
series,
|
||||
realIndex,
|
||||
i,
|
||||
j,
|
||||
w
|
||||
}) {
|
||||
const graphics = new Graphics(this.barCtx.ctx)
|
||||
strokeWidth = Array.isArray(strokeWidth)
|
||||
? strokeWidth[realIndex]
|
||||
: strokeWidth
|
||||
if (!strokeWidth) strokeWidth = 0
|
||||
|
||||
let shapeOpts = {
|
||||
barHeight,
|
||||
strokeWidth,
|
||||
barYPosition,
|
||||
x2,
|
||||
x1
|
||||
}
|
||||
|
||||
let newPath = this.getRoundedBars(w, shapeOpts, series, i, j)
|
||||
|
||||
let pathTo = graphics.move(x1, barYPosition)
|
||||
let pathFrom = graphics.move(x1, barYPosition)
|
||||
|
||||
if (w.globals.previousPaths.length > 0) {
|
||||
pathFrom = this.barCtx.getPreviousPath(realIndex, j, false)
|
||||
}
|
||||
|
||||
const y1 = barYPosition
|
||||
const y2 = barYPosition + barHeight
|
||||
|
||||
const sl = graphics.line(x1, y2 - strokeWidth)
|
||||
pathTo =
|
||||
pathTo +
|
||||
graphics.line(newPath.x2, y1) +
|
||||
newPath.pathWithRadius +
|
||||
graphics.line(newPath.x2, y2 - strokeWidth) +
|
||||
sl +
|
||||
sl +
|
||||
'z'
|
||||
|
||||
pathFrom =
|
||||
pathFrom +
|
||||
graphics.line(x1, y1) +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
sl +
|
||||
graphics.line(x1, y1)
|
||||
|
||||
if (w.config.chart.stacked) {
|
||||
this.barCtx.xArrj.push(newPath.x2)
|
||||
this.barCtx.xArrjF.push(Math.abs(x1 - newPath.x2))
|
||||
this.barCtx.xArrjVal.push(this.barCtx.series[i][j])
|
||||
}
|
||||
return {
|
||||
pathTo,
|
||||
pathFrom
|
||||
}
|
||||
}
|
||||
|
||||
/** getRoundedBars draws border radius for bars/columns
|
||||
* @memberof Bar
|
||||
* @param {object} w - chart context
|
||||
* @param {object} opts - consists several properties like barHeight/barWidth
|
||||
* @param {array} series - global primary series
|
||||
* @param {int} i - current iterating series's index
|
||||
* @param {int} j - series's j of i
|
||||
* @return {object} pathWithRadius - ending shape path string
|
||||
* newY/newX - which is calculated from existing x/y based on rounded border
|
||||
**/
|
||||
getRoundedBars(w, opts, series, i, j) {
|
||||
let graphics = new Graphics(this.barCtx.ctx)
|
||||
let radius = 0
|
||||
|
||||
const borderRadius = w.config.plotOptions.bar.borderRadius
|
||||
const borderRadiusIsArray = Array.isArray(borderRadius)
|
||||
if (borderRadiusIsArray) {
|
||||
const radiusIndex =
|
||||
i > borderRadius.length - 1 ? borderRadius.length - 1 : i
|
||||
radius = borderRadius[radiusIndex]
|
||||
} else {
|
||||
radius = borderRadius
|
||||
}
|
||||
|
||||
if (
|
||||
w.config.chart.stacked &&
|
||||
series.length > 1 &&
|
||||
i !== this.barCtx.radiusOnSeriesNumber &&
|
||||
!borderRadiusIsArray
|
||||
) {
|
||||
radius = 0
|
||||
}
|
||||
|
||||
if (this.barCtx.isHorizontal) {
|
||||
let pathWithRadius = ''
|
||||
let x2 = opts.x2
|
||||
|
||||
if (Math.abs(opts.x1 - opts.x2) < radius) {
|
||||
radius = Math.abs(opts.x1 - opts.x2)
|
||||
}
|
||||
|
||||
if (typeof series[i][j] !== 'undefined' || series[i][j] !== null) {
|
||||
let inverse = this.barCtx.isReversed
|
||||
? series[i][j] > 0
|
||||
: series[i][j] < 0
|
||||
|
||||
if (inverse) radius = radius * -1
|
||||
|
||||
x2 = x2 - radius
|
||||
|
||||
pathWithRadius =
|
||||
graphics.quadraticCurve(
|
||||
x2 + radius,
|
||||
opts.barYPosition,
|
||||
x2 + radius,
|
||||
opts.barYPosition + (!inverse ? radius : radius * -1)
|
||||
) +
|
||||
graphics.line(
|
||||
x2 + radius,
|
||||
opts.barYPosition +
|
||||
opts.barHeight -
|
||||
opts.strokeWidth -
|
||||
(!inverse ? radius : radius * -1)
|
||||
) +
|
||||
graphics.quadraticCurve(
|
||||
x2 + radius,
|
||||
opts.barYPosition + opts.barHeight - opts.strokeWidth,
|
||||
x2,
|
||||
opts.barYPosition + opts.barHeight - opts.strokeWidth
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
pathWithRadius,
|
||||
x2
|
||||
}
|
||||
} else {
|
||||
let pathWithRadius = ''
|
||||
let y2 = opts.y2
|
||||
|
||||
if (Math.abs(opts.y1 - opts.y2) < radius) {
|
||||
radius = Math.abs(opts.y1 - opts.y2)
|
||||
}
|
||||
|
||||
if (typeof series[i][j] !== 'undefined' || series[i][j] !== null) {
|
||||
let inverse = series[i][j] < 0
|
||||
|
||||
if (inverse) radius = radius * -1
|
||||
|
||||
y2 = y2 + radius
|
||||
|
||||
pathWithRadius =
|
||||
graphics.quadraticCurve(
|
||||
opts.barXPosition,
|
||||
y2 - radius,
|
||||
opts.barXPosition + (!inverse ? radius : radius * -1),
|
||||
y2 - radius
|
||||
) +
|
||||
graphics.line(
|
||||
opts.barXPosition +
|
||||
opts.barWidth -
|
||||
opts.strokeWidth -
|
||||
(!inverse ? radius : radius * -1),
|
||||
y2 - radius
|
||||
) +
|
||||
graphics.quadraticCurve(
|
||||
opts.barXPosition + opts.barWidth - opts.strokeWidth,
|
||||
y2 - radius,
|
||||
opts.barXPosition + opts.barWidth - opts.strokeWidth,
|
||||
y2
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
pathWithRadius,
|
||||
y2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkZeroSeries({ series }) {
|
||||
let w = this.w
|
||||
for (let zs = 0; zs < series.length; zs++) {
|
||||
let total = 0
|
||||
for (
|
||||
let zsj = 0;
|
||||
zsj < series[w.globals.maxValsInArrayIndex].length;
|
||||
zsj++
|
||||
) {
|
||||
total += series[zs][zsj]
|
||||
}
|
||||
if (total === 0) {
|
||||
this.barCtx.zeroSerieses.push(zs)
|
||||
}
|
||||
}
|
||||
|
||||
// After getting all zeroserieses, we need to ensure whether radiusOnSeriesNumber is not in that zeroseries array
|
||||
for (let s = series.length - 1; s >= 0; s--) {
|
||||
if (
|
||||
this.barCtx.zeroSerieses.indexOf(s) > -1 &&
|
||||
s === this.radiusOnSeriesNumber
|
||||
) {
|
||||
this.barCtx.radiusOnSeriesNumber -= 1
|
||||
}
|
||||
}
|
||||
|
||||
for (let s = series.length - 1; s >= 0; s--) {
|
||||
if (
|
||||
w.globals.collapsedSeriesIndices.indexOf(
|
||||
this.barCtx.radiusOnSeriesNumber
|
||||
) > -1
|
||||
) {
|
||||
this.barCtx.radiusOnSeriesNumber -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getXForValue(value, zeroW, zeroPositionForNull = true) {
|
||||
let xForVal = zeroPositionForNull ? zeroW : null
|
||||
if (typeof value !== 'undefined' && value !== null) {
|
||||
xForVal =
|
||||
zeroW +
|
||||
value / this.barCtx.invertedYRatio -
|
||||
(this.barCtx.isReversed ? value / this.barCtx.invertedYRatio : 0) * 2
|
||||
}
|
||||
return xForVal
|
||||
}
|
||||
|
||||
getYForValue(value, zeroH, zeroPositionForNull = true) {
|
||||
let yForVal = zeroPositionForNull ? zeroH : null
|
||||
if (typeof value !== 'undefined' && value !== null) {
|
||||
yForVal =
|
||||
zeroH -
|
||||
value / this.barCtx.yRatio[this.barCtx.yaxisIndex] +
|
||||
(this.barCtx.isReversed
|
||||
? value / this.barCtx.yRatio[this.barCtx.yaxisIndex]
|
||||
: 0) *
|
||||
2
|
||||
}
|
||||
return yForVal
|
||||
}
|
||||
|
||||
getGoalValues(type, zeroW, zeroH, i, j) {
|
||||
const w = this.w
|
||||
|
||||
let goals = []
|
||||
if (
|
||||
w.globals.seriesGoals[i] &&
|
||||
w.globals.seriesGoals[i][j] &&
|
||||
Array.isArray(w.globals.seriesGoals[i][j])
|
||||
) {
|
||||
w.globals.seriesGoals[i][j].forEach((goal) => {
|
||||
goals.push({
|
||||
[type]:
|
||||
type === 'x'
|
||||
? this.getXForValue(goal.value, zeroW, false)
|
||||
: this.getYForValue(goal.value, zeroH, false),
|
||||
attrs: goal
|
||||
})
|
||||
})
|
||||
}
|
||||
return goals
|
||||
}
|
||||
|
||||
drawGoalLine({
|
||||
barXPosition,
|
||||
barYPosition,
|
||||
goalX,
|
||||
goalY,
|
||||
barWidth,
|
||||
barHeight
|
||||
}) {
|
||||
let graphics = new Graphics(this.barCtx.ctx)
|
||||
const lineGroup = graphics.group({
|
||||
className: 'apexcharts-bar-goals-groups'
|
||||
})
|
||||
|
||||
let line = null
|
||||
if (this.barCtx.isHorizontal) {
|
||||
if (Array.isArray(goalX)) {
|
||||
goalX.forEach((goal) => {
|
||||
let sHeight =
|
||||
typeof goal.attrs.strokeHeight !== 'undefined'
|
||||
? goal.attrs.strokeHeight
|
||||
: barHeight / 2
|
||||
let y = barYPosition + sHeight + barHeight / 2
|
||||
|
||||
line = graphics.drawLine(
|
||||
goal.x,
|
||||
y - sHeight * 2,
|
||||
goal.x,
|
||||
y,
|
||||
goal.attrs.strokeColor ? goal.attrs.strokeColor : undefined,
|
||||
goal.attrs.strokeDashArray,
|
||||
goal.attrs.strokeWidth ? goal.attrs.strokeWidth : 2,
|
||||
goal.attrs.strokeLineCap
|
||||
)
|
||||
lineGroup.add(line)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(goalY)) {
|
||||
goalY.forEach((goal) => {
|
||||
let sWidth =
|
||||
typeof goal.attrs.strokeWidth !== 'undefined'
|
||||
? goal.attrs.strokeWidth
|
||||
: barWidth / 2
|
||||
let x = barXPosition + sWidth + barWidth / 2
|
||||
|
||||
line = graphics.drawLine(
|
||||
x - sWidth * 2,
|
||||
goal.y,
|
||||
x,
|
||||
goal.y,
|
||||
goal.attrs.strokeColor ? goal.attrs.strokeColor : undefined,
|
||||
goal.attrs.strokeDashArray,
|
||||
goal.attrs.strokeHeight ? goal.attrs.strokeHeight : 2,
|
||||
goal.attrs.strokeLineCap
|
||||
)
|
||||
lineGroup.add(line)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return lineGroup
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user