1

I've got a dynamically updated label that shows Y position animated on changing. I'd like to change the color and size of those digits that are animated during changing, depending on the direction of change. For example, if the number changes 150.41 --> 179.22 , I'd like "50.41" to be larger font and green color while transitioning to '79.22'& The first digit '1' should stay as it is. Here is my fiddle http://jsfiddle.net/nrzxysjd/

 series.addPoint([x, y], true, true);

            d = pathLine.d.split(' ');
            d[2] = yAxis.toPixels(y);
            d[5] = yAxis.toPixels(y);

            plotbandLabel.animate({
              y: yAxis.toPixels(y) - labelOffset
            }, {
              duration: 500,
              step: function() {
                this.attr({
                  text: yAxis.toValue(this.y + labelOffset).toFixed(2),
                  zIndex: 999999999
                })
              },
              complete: function() {
                this.attr({
                  text: y.toFixed(2),
                  zIndex: 999999999
                })
              }
            });

            pathLine.animate({
              d: d
            }, 500);
          }, 1000);
        }/*,
  • How is this animation should exactly look? Could you describe it step by step? Should the color and font size transition be smooth (gradual)? – Kamil Kulig Jan 17 '18 at 13:57
  • @KamilKulig Yes, it seems not to be very clear. Here is a short video how it should look like. [link](https://youtu.be/becpntDHads) – Fedor Alexandrovich Jan 19 '18 at 12:34
  • @KamilKulig The answer below is very close, but I'd like the changing of color happen only on the transition of numbers. Is it possible? – Fedor Alexandrovich Jan 19 '18 at 12:38

1 Answers1

0

First you need to enable HTML in the label:

plotbandLabel = this.renderer.label(
    (66).toFixed(2),
    chart.plotLeft + chart.plotWidth,
    yAxis.toPixels(66) - labelOffset,
    'rect',
    null,
    true // here
)

Then when you add a new point you need to calculate the delta with the last point and choose the CSS style accordingly:

let delta = y - series.yData[series.yData.length - 1],
style = { 'color': 'black', 'font-weight': 400 };
if (delta > 0) {
    style['color'] = 'green';
    style['font-weight'] = 700;
}
plotbandLabel.css(style);

This sets the style for the whole label. Now you need to override the style for the 1st digit:

plotbandLabel.animate({
    y: yAxis.toPixels(y) - labelOffset
}, {
    duration: 500,
    step: function () {
         //turn the value to string and split it into 2 parts
        let t = '' + yAxis.toValue(this.y + labelOffset).toFixed(2),
            firstLetter = t[0],
            otherletters = t.substr(1);

        this.attr({
            //put the 1st digit into its own span
            //and override the label style with inline style
            text: '<span style="color:black;font-weight:400">' 
                  + firstLetter + '</span>' + otherletters,
            zIndex: 999999999
        })
    },
    complete: function () {
        let t = '' + y.toFixed(2),
            firstLetter = t[0],
            otherletters = t.substr(1);
        this.attr({
            text: '<span style="color:black;font-weight:400">' 
                  + firstLetter + '</span>' + otherletters,
            zIndex: 999999999
        })
    }
});

Working example:

$(function() {
var chart, yAxis, plotbandLabel, pathLine
            labelOffset = 15;

  Highcharts.setOptions({
    global: {
      useUTC: false
    }
  });

  // Create the chart
  $('#container').highcharts('StockChart', {
    chart: {
      events: {
        load: function() {
          chart = this;
          yAxis = chart.yAxis[0]
            var series = chart.series[0],
            lastPoint = series.points[series.points.length - 1],
            initialValue = yAxis.toPixels(lastPoint.y),
            //plotbandLabel,
            plotLine,
            newY,
            d;

          /*var svgGroup = this.renderer.g()
            .attr({
              zIndex: 99
            }).add()*/

          pathLine = this.renderer.path([
              'M',
              chart.plotLeft, initialValue,
              'L',
              chart.plotWidth + chart.plotLeft,
              initialValue
            ])
            .attr({
              'stroke-width': 0,
              stroke: 'red'
            })
            .add();

          plotbandLabel = this.renderer.label(
            (66).toFixed(2),
            chart.plotLeft + chart.plotWidth,
            yAxis.toPixels(66) - labelOffset,
            'rect',
            null,
            true
          ).attr({
              align: 'right',
              fill: 'rgba(255, 0, 0, 0)',
              padding: 8,
              zIndex: 999
            })
            .add();
                 

          setInterval(function() {
            var x = (new Date()).getTime(),
              y = Math.round(Math.random() * 100 + 100),
      
             delta = y - series.yData[series.yData.length - 1],
             style = { 'color': 'black', 'font-weight': 400 };
            if (delta > 0) {
             style['color'] = 'green';
              style['font-weight'] = 700;
            }
            /*else if (delta < 0) {
             style['color'] = 'red';
              style['font-weight'] = 700;
            }*/
            
            plotbandLabel.css(style);
            series.addPoint([x, y], true, true);

            d = pathLine.d.split(' ');
            d[2] = yAxis.toPixels(y);
            d[5] = yAxis.toPixels(y);

            plotbandLabel.animate({
              y: yAxis.toPixels(y) - labelOffset
            }, {
              duration: 500,
              step: function() {
               let t = '' + yAxis.toValue(this.y + labelOffset).toFixed(2), 
                firstLetter = t[0], 
                otherletters = t.substr(1);
                
                this.attr({
                  text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>' 
                  +  otherletters ,
                  zIndex: 999999999
                })
              },
              complete: function() {
               let t = '' + y.toFixed(2), 
                firstLetter = t[0], 
                otherletters = t.substr(1);
                this.attr({
                  text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>' 
                  +  otherletters ,
                  zIndex: 999999999
                })
              }
            });

            pathLine.animate({
              d: d
            }, 500);
          }, 1000);
        }
      }
    },
    rangeSelector: {
      buttons: [{
        count: 1,
        type: 'minute',
        text: '1M'
      }, {
        count: 5,
        type: 'minute',
        text: '5M'
      }, {
        type: 'all',
        text: 'All'
      }],
      inputEnabled: false,
      selected: 0
    },
    title: {
      text: 'Live random data'
    },
    yAxis: [{
      opposite: false,
      title: {
        enabled: false
      }
    }],
    exporting: {
      enabled: false
    },
    series: [{
      name: 'Random data',
      data: (function() {
        // generate an array of random data
        var data = [],
          time = (new Date()).getTime(),
          i;

        for (i = -999; i <= 0; i += 1) {
          data.push([
            time + i * 1000,
            Math.round(Math.random() * 100)
          ]);
        }
        return data;
      }())
    }]
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.src.js"></script>

<div id="container" style="height: 400px; min-width: 310px">
  <div id="lastprice" style="position:absolute;x:0">
  </div>
</div>

Update:

var x = (new Date()).getTime(),
    y = Math.round(Math.random() * 100 + 100),
    prevVal = series.yData[series.yData.length - 1],
    //create string representations of current and old values of y
    y_str = '' + y.toFixed(2),
    prevVal_str = '' + prevVal.toFixed(2),
    delta = y - prevVal,
    style = { 'color': 'black', 'font-weight': 400 };
if (delta > 0) {
    style['color'] = 'green';
    style['font-weight'] = 700;
}
else if (delta < 0) {
    style['color'] = 'red';
    style['font-weight'] = 700;
}

//add padding of 0's to values so they will be the same length [1]
let max_len = Math.max(y_str.length, prevVal_str.length), padding = '';
for (let i = 0; i < max_len ;i++) padding += '0';
y_str = (padding + y_str).slice(max_len);
prevVal_str = (padding + prevVal_str).slice(max_len);
//find how many first digits did not change
for (var i = 0; i < max_len; i++) if (y_str[i] !== prevVal_str[i]) break;
//add some custom field to the label to store the number of first digits
plotbandLabel.num_digits_not_changed = i;

[1] padding code is based of this answer

Use num_digits_not_changed in the animation:

plotbandLabel.animate({
    y: yAxis.toPixels(y) - labelOffset
}, {
    duration: 500,
    step: function() {
        let t = '' + yAxis.toValue(this.y + labelOffset).toFixed(2),
            firstLetter = t.substr(0, this.num_digits_not_changed),
            otherletters = t.substr(this.num_digits_not_changed);

        this.attr({
            text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>'
            +  otherletters ,
            zIndex: 999999999
        })
    },
    complete: function() {
        let t = '' + y.toFixed(2),
            firstLetter = t.substr(0, this.num_digits_not_changed),
            otherletters = t.substr(this.num_digits_not_changed);
        this.attr({
            text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>'
            +  otherletters ,
            zIndex: 999999999
        })
    }
});

Working demo:

$(function() {
var chart, yAxis, plotbandLabel, pathLine
            labelOffset = 15;

  Highcharts.setOptions({
    global: {
      useUTC: false
    }
  });

  // Create the chart
  $('#container').highcharts('StockChart', {
    chart: {
      events: {
        load: function() {
          chart = this;
          yAxis = chart.yAxis[0]
            var series = chart.series[0],
            lastPoint = series.points[series.points.length - 1],
            initialValue = yAxis.toPixels(lastPoint.y),
            //plotbandLabel,
            plotLine,
            newY,
            d;

          /*var svgGroup = this.renderer.g()
            .attr({
              zIndex: 99
            }).add()*/

          pathLine = this.renderer.path([
              'M',
              chart.plotLeft, initialValue,
              'L',
              chart.plotWidth + chart.plotLeft,
              initialValue
            ])
            .attr({
              'stroke-width': 0,
              stroke: 'red'
            })
            .add();

          plotbandLabel = this.renderer.label(
            (66).toFixed(2),
            chart.plotLeft + chart.plotWidth,
            yAxis.toPixels(66) - labelOffset,
            'rect',
            null,
            true
          ).attr({
              align: 'right',
              fill: 'rgba(255, 0, 0, 0)',
              padding: 8,
              zIndex: 999
            })
            .add();
                 

          setInterval(function() {
            var x = (new Date()).getTime(),
              y = Math.round(Math.random() * 100 + 100),
      prevVal = series.yData[series.yData.length - 1],
            y_str = '' + y.toFixed(2),
            prevVal_str = '' + prevVal.toFixed(2),
             delta = y - prevVal,
             style = { 'color': 'black', 'font-weight': 400 };
            if (delta > 0) {
             style['color'] = 'green';
              style['font-weight'] = 700;
            }
            else if (delta < 0) {
             style['color'] = 'red';
              style['font-weight'] = 700;
            }
            
            let max_len = Math.max(y_str.length, prevVal_str.length), padding = '';
            for (let i = 0; i < max_len ;i++) padding += '0';
            y_str = (padding + y_str).slice(max_len);
            prevVal_str = (padding + prevVal_str).slice(max_len);
            for (var i = 0; i < max_len; i++) if (y_str[i] !== prevVal_str[i]) break;
            //console.log('number of first digits which did not change: ' + i);
            
            plotbandLabel.num_digits_not_changed = i;
            
            plotbandLabel.css(style);
            series.addPoint([x, y], true, true);

            d = pathLine.d.split(' ');
            d[2] = yAxis.toPixels(y);
            d[5] = yAxis.toPixels(y);

            plotbandLabel.animate({
              y: yAxis.toPixels(y) - labelOffset
            }, {
              duration: 500,
              step: function() {
               let t = '' + yAxis.toValue(this.y + labelOffset).toFixed(2), 
                firstLetter = t.substr(0, this.num_digits_not_changed), 
                otherletters = t.substr(this.num_digits_not_changed);
                
                this.attr({
                  text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>' 
                  +  otherletters ,
                  zIndex: 999999999
                })
              },
              complete: function() {
               let t = '' + y.toFixed(2), 
                firstLetter = t.substr(0, this.num_digits_not_changed), 
                otherletters = t.substr(this.num_digits_not_changed);
                this.attr({
                  text: '<span style="color:black;font-weight:400">'+firstLetter+'</span>' 
                  +  otherletters ,
                  zIndex: 999999999
                })
              }
            });

            pathLine.animate({
              d: d
            }, 500);
          }, 1000);
        }
      }
    },
    rangeSelector: {
      buttons: [{
        count: 1,
        type: 'minute',
        text: '1M'
      }, {
        count: 5,
        type: 'minute',
        text: '5M'
      }, {
        type: 'all',
        text: 'All'
      }],
      inputEnabled: false,
      selected: 0
    },
    title: {
      text: 'Live random data'
    },
    yAxis: [{
      opposite: false,
      title: {
        enabled: false
      }
    }],
    exporting: {
      enabled: false
    },
    series: [{
      name: 'Random data',
      data: (function() {
        // generate an array of random data
        var data = [],
          time = (new Date()).getTime(),
          i;

        for (i = -999; i <= 0; i += 1) {
          data.push([
            time + i * 1000,
            Math.round(Math.random() * 100)
          ]);
        }
        return data;
      }())
    }]
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.src.js"></script>

<div id="container" style="height: 400px; min-width: 310px">
  <div id="lastprice" style="position:absolute;x:0">
  </div>
</div>
Dmitry
  • 6,716
  • 14
  • 37
  • 39
  • Thank you for the detailed answer, but that's not the exact what I need. The thing I asked for was to change the style only for the digits that do change, but not for all except the first one. We need to style only the digits that will animate when the number changes. – Fedor Alexandrovich Jan 15 '18 at 16:03