apexcharts
Version:
A JavaScript Chart Library
213 lines (175 loc) • 5.48 kB
JavaScript
// @ts-check
import Animations from '../modules/Animations'
import Fill from '../modules/Fill'
import Filters from '../modules/Filters'
import Graphics from '../modules/Graphics'
import Markers from '../modules/Markers'
/**
* ApexCharts Scatter Class.
* This Class also handles bubbles chart as currently there is no major difference in drawing them,
* @module Scatter
**/
export default class Scatter {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx
this.w = w
this.initialAnim = this.w.config.chart.animations.enabled
this.anim = new Animations(this.w)
this.filters = new Filters(this.w)
this.fill = new Fill(this.w)
this.markers = new Markers(this.w, this.ctx)
this.graphics = new Graphics(this.w)
}
/**
* @param {Element} elSeries
* @param {number} j
* @param {Record<string, any>} opts
*/
draw(elSeries, j, opts) {
const w = this.w
const graphics = this.graphics
const realIndex = opts.realIndex
const pointsPos = opts.pointsPos
const zRatio = opts.zRatio
const elPointsMain = opts.elParent
const elPointsWrap = graphics.group({
class: `apexcharts-series-markers apexcharts-series-${w.config.chart.type}`,
})
elPointsWrap.attr('clip-path', `url(#gridRectMarkerMask${w.globals.cuid})`)
// Set up event delegation once on the group instead of per-point listeners
this.markers.setupMarkerDelegation(elPointsWrap)
if (Array.isArray(pointsPos.x)) {
for (let q = 0; q < pointsPos.x.length; q++) {
let dataPointIndex = j + 1
let shouldDraw = true
// a small hack as we have 2 points for the first val to connect it
if (j === 0 && q === 0) dataPointIndex = 0
if (j === 0 && q === 1) dataPointIndex = 1
let radius = w.globals.markers.size[realIndex]
if (zRatio !== Infinity) {
// means we have a bubble
const bubble = w.config.plotOptions.bubble
radius = w.seriesData.seriesZ[realIndex][dataPointIndex]
if (bubble.zScaling) {
radius /= zRatio
}
if (bubble.minBubbleRadius && radius < bubble.minBubbleRadius) {
radius = bubble.minBubbleRadius
}
if (bubble.maxBubbleRadius && radius > bubble.maxBubbleRadius) {
radius = bubble.maxBubbleRadius
}
}
const x = pointsPos.x[q]
const y = pointsPos.y[q]
radius = radius || 0
if (
y === null ||
typeof w.seriesData.series[realIndex][dataPointIndex] === 'undefined'
) {
shouldDraw = false
}
if (shouldDraw) {
const point = this.drawPoint(
x,
y,
radius,
realIndex,
dataPointIndex,
j,
)
elPointsWrap.add(point)
}
elPointsMain.add(elPointsWrap)
}
}
}
/**
* @param {number} x
* @param {number} y
* @param {number} radius
* @param {number} realIndex
* @param {number} dataPointIndex
* @param {number} j
*/
drawPoint(x, y, radius, realIndex, dataPointIndex, j) {
const w = this.w
const i = realIndex
const anim = this.anim
const filters = this.filters
const fill = this.fill
const markers = this.markers
const graphics = this.graphics
const markerConfig = markers.getMarkerConfig({
cssClass: 'apexcharts-marker',
seriesIndex: i,
dataPointIndex,
radius:
w.config.chart.type === 'bubble' ||
(w.globals.comboCharts &&
w.config.series[realIndex] &&
/** @type {Record<string,any>} */ (w.config.series[realIndex]).type === 'bubble')
? radius
: null,
})
let pathFillCircle = fill.fillPath({
seriesNumber: realIndex,
dataPointIndex,
color: markerConfig.pointFillColor,
patternUnits: 'objectBoundingBox',
value: w.seriesData.series[realIndex][j],
})
const el = graphics.drawMarker(x, y, markerConfig)
const _si = /** @type {Record<string,any>} */ (w.config.series[i])
if (_si.data[dataPointIndex]) {
if (_si.data[dataPointIndex].fillColor) {
pathFillCircle = _si.data[dataPointIndex].fillColor
}
}
el.attr({
fill: pathFillCircle,
})
if (w.config.chart.dropShadow.enabled) {
const dropShadow = w.config.chart.dropShadow
filters.dropShadow(el, dropShadow, realIndex)
}
if (this.initialAnim && !w.globals.dataChanged && !w.globals.resized) {
const speed = w.config.chart.animations.speed
anim.animateMarker(
el,
speed,
/** @type {any} */ (w.globals).easing,
() => {
window.setTimeout(() => {
anim.animationCompleted(el)
}, 100)
},
)
} else {
w.globals.animationEnded = true
}
el.attr({
rel: dataPointIndex,
j: dataPointIndex,
index: realIndex,
'default-marker-size': markerConfig.pSize,
})
filters.setSelectionFilter(el, realIndex, dataPointIndex)
el.node.classList.add('apexcharts-marker')
return el
}
/**
* @param {number} y
*/
centerTextInBubble(y) {
const w = this.w
y = y + parseInt(w.config.dataLabels.style.fontSize, 10) / 4
return {
y,
}
}
}