如何应用Apex图表多重渐变填充?

问题描述 投票:0回答:1

我正在使用 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 轴应包含零虚线注释。面积图的填充应为渐变类型,填充渐变应为水平类型,但其颜色根据垂直轴的零线(注释)变化。这意味着如果该线高于零线,则该部分应填充绿色,而低于零线的其他部分应填充红色。

angular typescript charts apexcharts ng-apexcharts
1个回答
0
投票
  1. 由于渐变是水平的,所以它的

    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
    });
    
  2. 对于垂直线渐变,库不认为

    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}));
    
  3. 最后,如果您希望不透明度从(左和右)边缘的

    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>

© www.soinside.com 2019 - 2024. All rights reserved.