我正在使用 Angular 18,这是我想要创建的图表: 原始图表图像
我的图表 ts 配置如下:
// 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--------------
但是经过所有这些配置,我得到的结果并不准确: 此代码的输出
期望: 我想要一个具有垂直 y 轴的图表,并且它的 y 轴应包含零虚线注释。面积图的填充应为渐变类型,填充渐变应为水平类型,但其颜色根据垂直轴的零线(注释)变化。这意味着如果该线高于零线,则该部分应填充绿色,而低于零线的其他部分应填充红色。
由于渐变是水平的,所以它的
colorSteps
也是水平的,也就是说你设置的offset
应该指定从图表区域左边距的0
到右边距的100
的水平位置。
为了设置这些水平偏移,您必须将它们计算为图表线与水平线(在本例中为 x 轴,
x
)相交点的 y = 0
分量,该分量应设置从绿色区域到红色区域,反之亦然。
直线与 x 轴的交点将被计算为直线每段的交点 与 x 轴,只要我们的图表线没有被平滑:
,这相当简单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
}
}
一旦我们有了这些交叉点,定义渐变就是将这些点设置为
option
对象中的颜色停止点:
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
});
对于垂直线渐变,库不认为
0
是图表区域的顶部和100
是图表区域的底部
图表区域,而是 0
线的最高点和 100
线的最低点。因此,如果您想要
颜色变化发生在y = 0
,颜色停止不应在 50,而应在
100 * Math.max(...seriesData)/(Math.max(...seriesData) - Math.min(...seriesData))
但是,由于线条的垂直切割可能与填充区域的水平切割不完全匹配,因此您也可以考虑对线条使用相同的水平渐变+色标,当然具有不同的不透明度:
option.stroke.fill.gradient.type = "horizontal";
option.stroke.fill.gradient.colorStops = colorStops.map(cs=>({...cs, opacity: 1}));
最后,如果您希望不透明度从(左和右)边缘的
0.5
线性变化
到中心的 0.3
,您必须在现有颜色停止处按比例计算不透明度,并且
还在偏移量 50
处添加新的颜色停止点,您将不透明度设置为 0.3
。
您可以循环遍历实际的
colorStops
数组以通过线性公式更改不透明度,
并确定 50
百分比偏移将位于哪个区域。
这是包含这三项更改的原始代码(必需部分),位于可运行的代码片段中:
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>