2

I'm exploring Highcharts and it seems like a very comprehensive package! I have a question regarding legend colors.

I notice that when I use a linear color gradient for a line, the color next to the legend (I believe it's called the symbolColor) may or may not show up, depending on the format I use:

1) symbolColor works fine: linearGradient: [ 00, 00, 00, 350 ]
2) symbolColor doesn't show up: linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }

I'd rather use the second format; it's easier and the first is not responsive.

Should the symbolColor always show up and this is an obscure issue/bug? Or am I doing something wrong? I also just noticed that if the marker is enabled in the plotOptions for series, symbolColor works fine with the second format. But I have many data points and don't want to use markers.

The fiddle graph has two lines -- purple and green -- and while the purple symbolColor using method (1) looks fine, you'll notice that the green symbolColor using method (2) doesn't show up.

https://jsfiddle.net/jwinkle/s6d9ah17/6/

Jim
  • 21
  • 4
  • I found an example where it does work: http://jsfiddle.net/q9ac21m7/ But I haven't figured out yet what the special sauce is to make it work. – Katharine Osborne Jan 31 '22 at 21:15
  • Looks like the issue is with the marker specifically. The difference between yours and the example I gave above is that yours has the marker disabled. By default, there's a marker shown in the legend with a line through it, and in the example I gave, the line is missing but the marker is there. Could be indicative of something buggy rather than a missed option or something. – Katharine Osborne Jan 31 '22 at 21:26
  • I have a solution, but it's sort of obscure, hang on... – Katharine Osborne Jan 31 '22 at 22:41

3 Answers3

0

In your HTML file, add:

<script src="https://code.highcharts.com/maps/modules/map.src.js"></script>

Above your chart code add:

Highcharts.addEvent(Highcharts.Chart.prototype, 'render', function() {
    this.series.forEach(function(series) {
      if (series.legendLine) {
        var pathDef = series.legendLine.attr('d').split(' ');
        pathDef[5] = parseFloat(pathDef[2]) + 0.0001;
        series.legendLine.attr({
          d: pathDef
        });
      }
    })
  });

The fix is in adding a smidge to your path definition (you can test this by going into the svg path element in dev tools and changing the last number in the 'd' attribute to a float:

<path fill="none" d="M 0 11 L 16 11" ... ></path>

becomes:

<path fill="none" d="M 0 11 L 16 11.1" ... ></path>

The function above goes through each series and massages the line data adding the fractional value in the right place. Total hack. However why the line does not appear if it does have this fractional value, I don't know. I would also test this across browsers to see that it works consistently as it may not (I tested in Chrome).

Here is a somewhat related issue where I cribbed the fix: https://www.highcharts.com/forum/viewtopic.php?t=38588 (Most of it is addressing a completely different problem, but the legend lines did show up so I investigated why).

If you don't want to use the extra library, you could probably write a native js function to find the legend lines and manipulate them in the same way.

Katharine Osborne
  • 6,571
  • 6
  • 32
  • 61
  • 1
    Thanks, @Katharine, that works(!), but *is* obscure! And I thought I was beginning to understand Highcharts. I have no idea what this is doing, but that's ok... stay tuned for a solution/hack I came up with. – Jim Feb 01 '22 at 04:24
  • 1
    There's a lot you can do with functions like this, such as dynamically re-rendering stuff, but as applied here it's just fixing the svg lines. So the problem you encountered could be a bug with Highcharts, or it could be a bug with svg rendering. It might be worth reporting it to Highcharts because what you were trying to do should have worked as expected the way you had it. – Katharine Osborne Feb 01 '22 at 16:46
0

In addition to @Katharine's solution, here's another I discovered.

If I enable the marker in one series (setting the radius to 0) and then put the data in a second series which is linked to the previous, it works. (If I do it all in one series, there is again no legend color.)

series: [{
    name: 'purple', lineWidth: 3, color: {
        linearGradient: [ 00, 00, 00, 350 ],
        //linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1, },
        stops: [
            [0, 'purple'], // start 
            [1, 'black'] // end
        ], 
    }, 
    data: [ 100, 95, 80, 60, 35, 50, 20, 10, 3, 2, 30, 40, ],
},{
    marker: {
        enabled: true,
        radius: 0,
    },
    name: 'green', color: 'green', lineWidth: 3, 
},{
    linkedTo: ':previous',
    lineWidth: 3, color: {
        //linearGradient: [ 00, 00, 00, 350 ], 
        linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1, },
        stops: [
            [0, 'green'], // start 
            [1, 'black'] // end
        ], 
    }, 
    data: [ 100, 100, 95, 80, 60, 35, 50, 20, 10, 3, 2, 30, ],
}],

Here's the "fixed" fiddle: https://jsfiddle.net/jwinkle/wztq78n1/6/

I reported the original issue as a possible bug to Highcharts, and they confirmed that it's an SVG rendering problem out of their control.

Jim
  • 21
  • 4
0

Here, you can find a nice explanation of the problem.


The difference results from the fact that Highcharts uses 'userSpaceOnUse' as a default value for gradientUnits if a gradient is defined as an array.

Highcharts source code:

// Keep < 2.2 kompatibility
if (isArray(gradAttr)) {
  (colorOptions as any)[gradName] = gradAttr = {
    x1: gradAttr[0] as number,
    y1: gradAttr[1] as number,
    x2: gradAttr[2] as number,
    y2: gradAttr[3] as number,
    gradientUnits: 'userSpaceOnUse'
  };
}

As a possible solution, you can set gradientUnits also for linearGradient defined as an object:

color: {
  linearGradient: {
    ...,
    gradientUnits: 'userSpaceOnUse'
  },
  ...
}

Live demo: http://jsfiddle.net/BlackLabel/yz6sagxj/


Or modify the paths very slightly so that they are not perfectly horizontal

chart: {
  events: {
    render: function() {
      this.series.forEach(function(series) {
        if (series.legendLine) {
          var pathDef = series.legendLine.attr('d').split(' ');
          pathDef[5] = parseFloat(pathDef[2]) + 0.0001;
          series.legendLine.attr({
            d: pathDef
          });
        }
      })
    }
  }
}

Live demo: http://jsfiddle.net/BlackLabel/L1kzg0cn/

Github thread: https://github.com/highcharts/highcharts/issues/1936

ppotaczek
  • 36,341
  • 2
  • 14
  • 24
  • Thanks for responding, @ppotaczek, but unfortunately one can't use '1' as in your demo with 'userSpaceOnUse'. One of the other answers to the post you referenced explained "But sometimes userSpaceOnUse is a pain, because it spreads the interpolation over the entire canvas, instead of just the line." Here's a fiddle using 1 and 400 for x2; you can see that it's only a gradient when using 400. Thanks also for simplifying my example (although I re-disabled the marker since as I mentioned originally, things work fine if it's enabled, but I don't want it). https://jsfiddle.net/jwinkle/a279j1cf/17/ – Jim Feb 01 '22 at 19:58
  • Hi @Jim, I am glad that I can help. I am not sure what exactly you want to achieve. With `x2: 400` you have a gradient on your chart and the legend symbol is shown. Do you want to also have the gradient on the legend symbol? – ppotaczek Feb 02 '22 at 09:34
  • I'd rather use the second format; it's easier and the first (array format) is not responsive. In addition, the array format is no longer documented; it seems the HC folks would prefer we use the object format. I reported this as a potential issue here: https://github.com/highcharts/highcharts/issues/16947 – Jim Feb 03 '22 at 03:55
  • Yes, you are right. The array format works only because of backward compatibility. Please specify how your chart should look like and I am sure that we will figure something out :) – ppotaczek Feb 03 '22 at 09:55
  • 1
    Thanks, @ppotaczek... I found a good workaround by linking to a dummy series, which I posted here as another solution. https://stackoverflow.com/a/70935607/18084691 – Jim Feb 03 '22 at 16:19