I am using Angular 18 and this is the chart That I want to create: Orignal Chart image
My ts configuration for chart is given below:
// chart--------------
public chartData: {
series: ApexAxisChartSeries;
chart: ApexChart;
yaxis: ApexYAxis;
xaxis: ApexXAxis;
annotations: ApexAnnotations;
fill: ApexFill;
stroke: ApexStroke;
markers: ApexMarkers;
dataLabels:ApexDataLabels
grid:ApexGrid
plotOptions:ApexPlotOptions
colors:string[]
};
@ViewChild("chart") chart!: ChartComponent;
constructor(private renderer: Renderer2) {
const seriesData = [50, 40, 35, -10, -40, 60, -70];
this.chartData = {
series: [
{
name: 'Profit/Loss',
data: seriesData,
},
],
chart: {
type: 'area',
height: 400,
foreColor: '#2f2f2f',
toolbar:{
show:false
}
},
yaxis: {
opposite: true,
min: -100,
max: 100,
tickAmount: 20, // Creates gaps of 10%
labels: {
formatter: (value) => {
return value >= 0 ? `${value}%` : `${value}%`; // Return the value
},
style: {
// Apply the class based on value
colors:[ '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600','#565656','#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD',]
}
}
},
xaxis: {
labels: {
show: true, // Hide the x-axis labels
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
annotations: {
yaxis: [
{
y: 0,
borderColor: '#000',
strokeDashArray: 5, // Create the dotted line at 0%
},
],
points: [
{
x: 360, // End of the data series
y: -70, // Example value of profit/loss at the end of the line
label: {
text: 'Profit: 70%',
borderColor: '#00E396',
style: {
color: '#fff',
background: '#00E396',
},
},
},
],
},
fill: {
type: 'gradient',
gradient: {
type:'horizontal',
shadeIntensity: 1,
inverseColors: false,
opacityFrom: 0.7,
opacityTo: 0.9,
gradientToColors:undefined,
stops: [0, 50, 100],
colorStops: [
{
offset: 0,
color: "#19FB9B", // Green color at 0%
opacity: 0.5
},
{
offset: 50,
color: "#19FB9B", // Green color transition
opacity: 0.3
},
{
offset: 50,
color: "#EE1600", // Red color transition
opacity: 0.3
},
{
offset: 100,
color: "#EE1600", // Red color at 100%
opacity: 0.5
}
]
},
},
stroke: {
curve: 'straight',
width: 3,
fill: {
type: 'gradient',
gradient: {
type:'vertical',
shadeIntensity: 1,
colorStops: [
{
offset: 0,
color: "#19FB9B", // Green color at 0%
opacity: 1
},
{
offset: 50,
color: "#19FB9B", // Green color transition
opacity: 1
},
{
offset: 50,
color: "#EE1600", // Red color transition
opacity: 1
},
{
offset: -100,
color: "#EE1600", // Red color at 100%
opacity: 1
}
]
}
}
},
colors: ['#00FFAF'],
markers: {
size: 0, // Set the marker size to 0 to hide the points
},
dataLabels: {
enabled: false
},
grid: {
row: {
colors: ["transparent", "transparent"],
opacity: 0.5,
},
borderColor:'#2f2f2f'
},
plotOptions:{
area: {
fillTo: 'end'
}
}
};
}
// chart--------------
But After all this configuration the result I got is not accurate: output of this code
Expectations: I want a graph that has a vertical y-axis and it's y-axis should contain a zero dotted line annotation. The area chart's fill should be of gradient type and the gradient of fill should be of horizontal type but it's color changes based on zero line(annotation) of vertical axis. It means that If the line is above than the zero line then that part should be green filled but the other part which is below the zero line should be red filled.
Since the gradient is horizontal, its colorSteps
are also horizontal, that is, the offset
s you set should specify a horizontal position from 0
to the left margin of the chart area to 100
at its right margin.
In order to set these horizontal offsets, you have to compute them as the x
components of the intersection points of the chart line with the horizontal line (in this case the x axis, y = 0
), that should set the change from the green to the red zones and vice versa.
The intersections of the line with the x axis are to be computed as the intersection of each segment of the line with the x axis, which is rather simple as long as our chart line is not smoothed out:
const zeroYIntersections = [];
const nIntervals = seriesData.length - 1; // equidistant intervals for category type axis
for(let i = 0; i < nIntervals; i++){
const prod = seriesData[i] * seriesData[i+1];
if(prod <= 0){ // if the sign changes
zeroYIntersections.push(100*(i + (seriesData[i]/(seriesData[i] - seriesData[i+1])))/nIntervals);
// by 100 * x / nIntervals we set the "units" of these coordinates as percent from 0 to the left to 100 at the right
}
}
Once we have these intersections, defining the gradient is just setting these points as color stops in the option
object:
const colorPlus = "#19FB9B",
colorMinus = "#EE1600",
otherColor = (col) => col === colorPlus ? colorMinus : colorPlus;
let currentColor = seriesData.find(x => x !== 0) > 0 ? colorPlus : colorMinus;
const colorStops = [{
offset: 0,
color: currentColor,
opacity: 0.5
}];
for(let intP of zeroYIntersections){
colorStops.push({
offset: intP,
color: currentColor,
opacity: 0.3
});
currentColor = otherColor(currentColor);
colorStops.push({
offset: intP,
color: currentColor,
opacity: 0.3
});
}
colorStops.push({
offset: 100,
color: currentColor,
opacity: 0.5
});
For the vertical line gradient, the library doesn't consider 0
to be the bottom of the chart area and 100
the top of the
chart area, but rather 0
the lowest point of the line and 100
the highest point of the line. Thus, if you want the
color change to happen at y = 0
, the color stop should not be at 50, but at
100 * Math.max(...seriesData)/(Math.max(...seriesData) - Math.min(...seriesData))
However, since the vertical cut of the line might not perfectly match the horizontal cut of the filled area, you may consider using the same horizontal gradient + color stops for the line too, with different opacities, of course:
option.stroke.fill.gradient.type = "horizontal";
option.stroke.fill.gradient.colorStops = colorStops.map(cs=>({...cs, opacity: 1}));
And finally, if you want the opacity to vary linearly from say 0.5
at the (left and right) margins
to 0.3
at the center, you have to compute the opacities proportionally at the existing color stops and
also add a new color stop at offset 50
where you set the opacity to 0.3
.
You may loop through the actual colorStops
array to change the opacities by the linear formula,
and also identify in which zone the 50
percent offset will be.
Here's the (required part of the) original code with these three changes, in a runnable snippet:
const seriesData = [50, 40, 35, -10, -40, 60, -70];
const verticalOffset = 100 * Math.max(...seriesData)/(Math.max(...seriesData) - Math.min(...seriesData));
const option = {
series: [
{
name: 'Profit/Loss',
data: seriesData,
},
],
chart: {
type: 'area',
height: 400,
foreColor: '#2f2f2f',
toolbar:{
show:false
}
},
yaxis: {
opposite: true,
min: -100,
max: 100,
tickAmount: 20, // Creates gaps of 10%
labels: {
formatter: (value) => {
return value >= 0 ? `${value}%` : `${value}%`; // Return the value
},
style: {
// Apply the class based on value
colors:[ '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600', '#EE1600','#565656','#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD', '#00FFBD',]
}
}
},
xaxis: {
labels: {
show: true, // Hide the x-axis labels
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
annotations: {
yaxis: [
{
y: 0,
borderColor: '#000',
strokeDashArray: 5, // Create the dotted line at 0%
},
],
points: [
{
x: 360, // End of the data series
y: -70, // Example value of profit/loss at the end of the line
label: {
text: 'Profit: 70%',
borderColor: '#00E396',
style: {
color: '#fff',
background: '#00E396',
},
},
},
],
},
fill: {
type: 'gradient',
gradient: {
type:'horizontal',
// will set colorStops later
},
},
stroke: {
curve: 'straight',
width: 3,
fill: {
type: 'gradient',
gradient: {
type:'vertical',
shadeIntensity: 1,
colorStops: [
{
offset: 0,
color: "#19FB9B", // Green color at 0%
opacity: 1
},
{
offset: verticalOffset,
color: "#19FB9B", // Green color transition
opacity: 1
},
{
offset: verticalOffset,
color: "#EE1600", // Red color transition
opacity: 1
},
{
offset: -100,
color: "#EE1600", // Red color at 100%
opacity: 1
}
]
}
}
},
colors: ['#00FFAF'],
markers: {
size: 0, // Set the marker size to 0 to hide the points
},
dataLabels: {
enabled: false
},
grid: {
row: {
colors: ["transparent", "transparent"],
opacity: 0.5,
},
borderColor:'#2f2f2f'
},
plotOptions:{
area: {
fillTo: 'end'
}
}
};
const zeroYIntersections = [];
const nIntervals = seriesData.length - 1; // equidistant intervals for category type axis
for(let i = 0; i < nIntervals; i++){
const prod = seriesData[i] * seriesData[i+1];
if(prod <= 0){ // if the sign changes
zeroYIntersections.push(100*(i + (seriesData[i]/(seriesData[i] - seriesData[i+1])))/nIntervals);
}
}
const colorPlus = "#19FB9B",
colorMinus = "#EE1600",
otherColor = (col) => col === colorPlus ? colorMinus : colorPlus;
let currentColor = seriesData.find(x => x !== 0) > 0 ? colorPlus : colorMinus;
const colorStops = [{
offset: 0,
color: currentColor,
opacity: 0.5
}];
for(let intP of zeroYIntersections){
colorStops.push({
offset: intP,
color: currentColor,
opacity: 0.3
});
currentColor = otherColor(currentColor);
colorStops.push({
offset: intP,
color: currentColor,
opacity: 0.3
});
}
colorStops.push({
offset: 100,
color: currentColor,
opacity: 0.5
});
// use the horizontal gradient for stroke too
//option.stroke.fill.gradient.type = "horizontal";
//option.stroke.fill.gradient.colorStops = colorStops.map(cs=>({...cs, opacity: 1}));
// set the opacities to vary linearly from 0.5 at the margins to 0.3 at the center
const opacityMargins = 0.5;
const opacityCenter = 0.3;
let indexCenter = -1, colorCenter = '';
colorStops.forEach((colorStop, index) => {
const {offset, color} = colorStop;
colorStop.opacity = opacityMargins + Math.min(offset, 100 - offset) / 50 * (opacityCenter - opacityMargins);
if(indexCenter === -1 && offset > 50){
indexCenter = index;
colorCenter = color;
}
});
colorStops.splice(indexCenter, 0, {
offset: 50,
color: colorCenter,
opacity: opacityCenter
});
option.fill.gradient.colorStops = colorStops;
const chart = new ApexCharts(document.querySelector("#chart"), option);
chart.render();
<div id="chart" style="background-color: rgb(0, 0, 0)"></div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>