UNPKG

apexcharts

Version:

A JavaScript Chart Library

513 lines (439 loc) 13.1 kB
import Graphics from './Graphics' import Utils from '../utils/Utils' /** * ApexCharts Fill Class for setting fill options of the paths. * * @module Fill **/ class Fill { constructor(ctx) { this.ctx = ctx this.w = ctx.w this.opts = null this.seriesIndex = 0 this.patternIDs = [] } clippedImgArea(params) { let w = this.w let cnf = w.config let svgW = parseInt(w.globals.gridWidth, 10) let svgH = parseInt(w.globals.gridHeight, 10) let size = svgW > svgH ? svgW : svgH let fillImg = params.image let imgWidth = 0 let imgHeight = 0 if ( typeof params.width === 'undefined' && typeof params.height === 'undefined' ) { if ( cnf.fill.image.width !== undefined && cnf.fill.image.height !== undefined ) { imgWidth = cnf.fill.image.width + 1 imgHeight = cnf.fill.image.height } else { imgWidth = size + 1 imgHeight = size } } else { imgWidth = params.width imgHeight = params.height } let elPattern = document.createElementNS(w.globals.SVGNS, 'pattern') Graphics.setAttrs(elPattern, { id: params.patternID, patternUnits: params.patternUnits ? params.patternUnits : 'userSpaceOnUse', width: imgWidth + 'px', height: imgHeight + 'px', }) let elImage = document.createElementNS(w.globals.SVGNS, 'image') elPattern.appendChild(elImage) elImage.setAttributeNS(window.SVG.xlink, 'href', fillImg) Graphics.setAttrs(elImage, { x: 0, y: 0, preserveAspectRatio: 'none', width: imgWidth + 'px', height: imgHeight + 'px', }) elImage.style.opacity = params.opacity w.globals.dom.elDefs.node.appendChild(elPattern) } getSeriesIndex(opts) { const w = this.w const cType = w.config.chart.type if ( ((cType === 'bar' || cType === 'rangeBar') && w.config.plotOptions.bar.distributed) || cType === 'heatmap' || cType === 'treemap' ) { this.seriesIndex = opts.seriesNumber } else { this.seriesIndex = opts.seriesNumber % w.globals.series.length } return this.seriesIndex } computeColorStops(data, multiColorConfig) { const w = this.w let maxPositive = null let minNegative = null for (let value of data) { if (value >= multiColorConfig.threshold) { if (maxPositive === null || value > maxPositive) { maxPositive = value } } else { if (minNegative === null || value < minNegative) { minNegative = value } } } if (maxPositive === null) { maxPositive = multiColorConfig.threshold } if (minNegative === null) { minNegative = multiColorConfig.threshold } let totalRange = maxPositive - multiColorConfig.threshold + (multiColorConfig.threshold - minNegative) if (totalRange === 0) { totalRange = 1 } let negativePercentage = ((multiColorConfig.threshold - minNegative) / totalRange) * 100 let offset = 100 - negativePercentage offset = Math.max(0, Math.min(offset, 100)) return [ { offset: offset, color: multiColorConfig.colorAboveThreshold, opacity: w.config.fill.opacity, }, { offset: 0, color: multiColorConfig.colorBelowThreshold, opacity: w.config.fill.opacity, }, ] } fillPath(opts) { let w = this.w this.opts = opts let cnf = this.w.config let pathFill let patternFill, gradientFill this.seriesIndex = this.getSeriesIndex(opts) const drawMultiColorLine = cnf.plotOptions.line.colors.colorAboveThreshold && cnf.plotOptions.line.colors.colorBelowThreshold let fillColors = this.getFillColors() let fillColor = fillColors[this.seriesIndex] //override fillcolor if user inputted color with data if (w.globals.seriesColors[this.seriesIndex] !== undefined) { fillColor = w.globals.seriesColors[this.seriesIndex] } if (typeof fillColor === 'function') { fillColor = fillColor({ seriesIndex: this.seriesIndex, dataPointIndex: opts.dataPointIndex, value: opts.value, w, }) } let fillType = opts.fillType ? opts.fillType : this.getFillType(this.seriesIndex) let fillOpacity = Array.isArray(cnf.fill.opacity) ? cnf.fill.opacity[this.seriesIndex] : cnf.fill.opacity // when line colors needs to be different based on values, we use gradient config to achieve this const useGradient = fillType === 'gradient' || drawMultiColorLine if (opts.color) { fillColor = opts.color } if ( w.config.series[this.seriesIndex]?.data?.[opts.dataPointIndex]?.fillColor ) { fillColor = w.config.series[this.seriesIndex]?.data?.[opts.dataPointIndex] ?.fillColor } // in case a color is undefined, fallback to white color to prevent runtime error if (!fillColor) { fillColor = '#fff' console.warn('undefined color - ApexCharts') } let defaultColor = fillColor if (fillColor.indexOf('rgb') === -1) { if (fillColor.indexOf('#') === -1) { defaultColor = fillColor } else if (fillColor.length < 9) { // if the hex contains alpha and is of 9 digit, skip the opacity defaultColor = Utils.hexToRgba(fillColor, fillOpacity) } } else { if (fillColor.indexOf('rgba') > -1) { fillOpacity = Utils.getOpacityFromRGBA(fillColor) } else { defaultColor = Utils.hexToRgba(Utils.rgb2hex(fillColor), fillOpacity) } } if (opts.opacity) fillOpacity = opts.opacity if (fillType === 'pattern') { patternFill = this.handlePatternFill({ fillConfig: opts.fillConfig, patternFill, fillColor, fillOpacity, defaultColor, }) } if (useGradient) { let colorStops = [...cnf.fill.gradient.colorStops] || [] let type = cnf.fill.gradient.type if (drawMultiColorLine) { colorStops[this.seriesIndex] = this.computeColorStops( w.globals.series[this.seriesIndex], cnf.plotOptions.line.colors ) type = 'vertical' } gradientFill = this.handleGradientFill({ type, fillConfig: opts.fillConfig, fillColor, fillOpacity, colorStops, i: this.seriesIndex, }) } if (fillType === 'image') { let imgSrc = cnf.fill.image.src let patternID = opts.patternID ? opts.patternID : '' const patternKey = `pattern${w.globals.cuid}${ opts.seriesNumber + 1 }${patternID}` if (this.patternIDs.indexOf(patternKey) === -1) { this.clippedImgArea({ opacity: fillOpacity, image: Array.isArray(imgSrc) ? opts.seriesNumber < imgSrc.length ? imgSrc[opts.seriesNumber] : imgSrc[0] : imgSrc, width: opts.width ? opts.width : undefined, height: opts.height ? opts.height : undefined, patternUnits: opts.patternUnits, patternID: patternKey, }) this.patternIDs.push(patternKey) } pathFill = `url(#${patternKey})` } else if (useGradient) { pathFill = gradientFill } else if (fillType === 'pattern') { pathFill = patternFill } else { pathFill = defaultColor } // override pattern/gradient if opts.solid is true if (opts.solid) { pathFill = defaultColor } return pathFill } getFillType(seriesIndex) { const w = this.w if (Array.isArray(w.config.fill.type)) { return w.config.fill.type[seriesIndex] } else { return w.config.fill.type } } getFillColors() { const w = this.w const cnf = w.config const opts = this.opts let fillColors = [] if (w.globals.comboCharts) { if (w.config.series[this.seriesIndex].type === 'line') { if (Array.isArray(w.globals.stroke.colors)) { fillColors = w.globals.stroke.colors } else { fillColors.push(w.globals.stroke.colors) } } else { if (Array.isArray(w.globals.fill.colors)) { fillColors = w.globals.fill.colors } else { fillColors.push(w.globals.fill.colors) } } } else { if (cnf.chart.type === 'line') { if (Array.isArray(w.globals.stroke.colors)) { fillColors = w.globals.stroke.colors } else { fillColors.push(w.globals.stroke.colors) } } else { if (Array.isArray(w.globals.fill.colors)) { fillColors = w.globals.fill.colors } else { fillColors.push(w.globals.fill.colors) } } } // colors passed in arguments if (typeof opts.fillColors !== 'undefined') { fillColors = [] if (Array.isArray(opts.fillColors)) { fillColors = opts.fillColors.slice() } else { fillColors.push(opts.fillColors) } } return fillColors } handlePatternFill({ fillConfig, patternFill, fillColor, fillOpacity, defaultColor, }) { let fillCnf = this.w.config.fill if (fillConfig) { fillCnf = fillConfig } const opts = this.opts let graphics = new Graphics(this.ctx) let patternStrokeWidth = Array.isArray(fillCnf.pattern.strokeWidth) ? fillCnf.pattern.strokeWidth[this.seriesIndex] : fillCnf.pattern.strokeWidth let patternLineColor = fillColor if (Array.isArray(fillCnf.pattern.style)) { if (typeof fillCnf.pattern.style[opts.seriesNumber] !== 'undefined') { let pf = graphics.drawPattern( fillCnf.pattern.style[opts.seriesNumber], fillCnf.pattern.width, fillCnf.pattern.height, patternLineColor, patternStrokeWidth, fillOpacity ) patternFill = pf } else { patternFill = defaultColor } } else { patternFill = graphics.drawPattern( fillCnf.pattern.style, fillCnf.pattern.width, fillCnf.pattern.height, patternLineColor, patternStrokeWidth, fillOpacity ) } return patternFill } handleGradientFill({ type, fillColor, fillOpacity, fillConfig, colorStops, i, }) { let fillCnf = this.w.config.fill if (fillConfig) { fillCnf = { ...fillCnf, ...fillConfig, } } const opts = this.opts let graphics = new Graphics(this.ctx) let utils = new Utils() type = type || fillCnf.gradient.type let gradientFrom = fillColor let gradientTo let opacityFrom = fillCnf.gradient.opacityFrom === undefined ? fillOpacity : Array.isArray(fillCnf.gradient.opacityFrom) ? fillCnf.gradient.opacityFrom[i] : fillCnf.gradient.opacityFrom if (gradientFrom.indexOf('rgba') > -1) { opacityFrom = Utils.getOpacityFromRGBA(gradientFrom) } let opacityTo = fillCnf.gradient.opacityTo === undefined ? fillOpacity : Array.isArray(fillCnf.gradient.opacityTo) ? fillCnf.gradient.opacityTo[i] : fillCnf.gradient.opacityTo if ( fillCnf.gradient.gradientToColors === undefined || fillCnf.gradient.gradientToColors.length === 0 ) { if (fillCnf.gradient.shade === 'dark') { gradientTo = utils.shadeColor( parseFloat(fillCnf.gradient.shadeIntensity) * -1, fillColor.indexOf('rgb') > -1 ? Utils.rgb2hex(fillColor) : fillColor ) } else { gradientTo = utils.shadeColor( parseFloat(fillCnf.gradient.shadeIntensity), fillColor.indexOf('rgb') > -1 ? Utils.rgb2hex(fillColor) : fillColor ) } } else { if (fillCnf.gradient.gradientToColors[opts.seriesNumber]) { const gToColor = fillCnf.gradient.gradientToColors[opts.seriesNumber] gradientTo = gToColor if (gToColor.indexOf('rgba') > -1) { opacityTo = Utils.getOpacityFromRGBA(gToColor) } } else { gradientTo = fillColor } } if (fillCnf.gradient.gradientFrom) { gradientFrom = fillCnf.gradient.gradientFrom } if (fillCnf.gradient.gradientTo) { gradientTo = fillCnf.gradient.gradientTo } if (fillCnf.gradient.inverseColors) { let t = gradientFrom gradientFrom = gradientTo gradientTo = t } if (gradientFrom.indexOf('rgb') > -1) { gradientFrom = Utils.rgb2hex(gradientFrom) } if (gradientTo.indexOf('rgb') > -1) { gradientTo = Utils.rgb2hex(gradientTo) } return graphics.drawGradient( type, gradientFrom, gradientTo, opacityFrom, opacityTo, opts.size, fillCnf.gradient.stops, colorStops, i ) } } export default Fill