admin管理员组

文章数量:1304698

I'm trying to add an arrow to the end of my Highcharts line-chart. I've already looked through similar questions but the solutions don't seem to work:

The result i'm looking for (added in colors),

I'm trying to add an arrow to the end of my Highcharts line-chart. I've already looked through similar questions but the solutions don't seem to work:

The result i'm looking for (added in colors),

Share Improve this question edited May 23, 2017 at 12:15 CommunityBot 11 silver badge asked Jul 15, 2016 at 9:45 Vito MelchionnnaVito Melchionnna 1071 silver badge8 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 3

You should wrap the drawGraph method and add the code for arrow. It can be realised by path method.

(function(H) {
        H.wrap(H.Series.prototype, 'drawGraph', function(proceed) {

          // Now apply the original function with the original arguments, 
          // which are sliced off this function's arguments
          proceed.apply(this, Array.prototype.slice.call(arguments, 1));

          var arrowLength = 15,
            arrowWidth = 9,
            series = this,
            data = series.data,
            len = data.length,
            segments = data,
            lastSeg = segments[segments.length - 1],
            path = [];


          var lastPoint = null;
          var nextLastPoint = null;

          if (lastSeg.y == 0) {
            lastPoint = segments[segments.length - 2];
            nextLastPoint = segments[segments.length - 1];
          } else {
            lastPoint = segments[segments.length - 1];
            nextLastPoint = segments[segments.length - 2];
          }

          var angle = Math.atan((lastPoint.plotX - nextLastPoint.plotX) /
            (lastPoint.plotY - nextLastPoint.plotY));

          if (angle < 0) angle = Math.PI + angle;

          path.push('M', lastPoint.plotX, lastPoint.plotY);

          if (lastPoint.plotX > nextLastPoint.plotX) {
            path.push(
              'L',
              lastPoint.plotX + arrowWidth * Math.cos(angle),
              lastPoint.plotY - arrowWidth * Math.sin(angle)
            );
            path.push(
              lastPoint.plotX + arrowLength * Math.sin(angle),
              lastPoint.plotY + arrowLength * Math.cos(angle)
            );
            path.push(
              lastPoint.plotX - arrowWidth * Math.cos(angle),
              lastPoint.plotY + arrowWidth * Math.sin(angle),
              'Z'
            );
          } else {
            path.push(
              'L',
              lastPoint.plotX - arrowWidth * Math.cos(angle),
              lastPoint.plotY + arrowWidth * Math.sin(angle)
            );
            path.push(
              lastPoint.plotX - arrowLength * Math.sin(angle),
              lastPoint.plotY - arrowLength * Math.cos(angle)
            );
            path.push(
              lastPoint.plotX + arrowWidth * Math.cos(angle),
              lastPoint.plotY - arrowWidth * Math.sin(angle),
              'Z'
            );
          }

          series.chart.renderer.path(path)
            .attr({
              fill: series.color
            })
            .add(series.group);

        });
      }(Highcharts));

Example:

  • http://jsfiddle/vyfsf1ft/

(UPDATE)

The previous solution is not quite responsive because it redraws the arrow on the chart every single time when the browser is resized.

  • Old Example: http://jsfiddle/vyfsf1ft/

However I found out how to make the chart with the arrow fully responsive:

(function(H) {

    var arrowCheck = false,
        pathTag;

    H.wrap(H.Series.prototype, 'drawGraph', function(proceed) {

    // Now apply the original function with the original arguments, 
    // which are sliced off this function's arguments

    proceed.apply(this, Array.prototype.slice.call(arguments, 1));

    var arrowLength = 15,
        arrowWidth = 9,
        series = this,
        data = series.data,
        len = data.length,
        segments = data,
        lastSeg = segments[segments.length - 1],
        path = [],
        lastPoint = null,
        nextLastPoint = null;

    if (lastSeg.y == 0) {
       lastPoint = segments[segments.length - 2];
       nextLastPoint = segments[segments.length - 1];
    } else {
       lastPoint = segments[segments.length - 1];
       nextLastPoint = segments[segments.length - 2];
    }

    var angle = Math.atan((lastPoint.plotX - nextLastPoint.plotX) /
        (lastPoint.plotY - nextLastPoint.plotY));

    if (angle < 0) angle = Math.PI + angle;

    path.push('M', lastPoint.plotX, lastPoint.plotY);

    if (lastPoint.plotX > nextLastPoint.plotX) {

       if (arrowCheck === true) {

         pathTag = document.getElementById("arrow");
         if (pathTag != null) {
           pathTag.remove(pathTag);
       }
     }

     path.push(
        'L',
        lastPoint.plotX + arrowWidth * Math.cos(angle),
        lastPoint.plotY - arrowWidth * Math.sin(angle)
     );
     path.push(
        lastPoint.plotX + arrowLength * Math.sin(angle),
        lastPoint.plotY + arrowLength * Math.cos(angle)
     );
     path.push(
        lastPoint.plotX - arrowWidth * Math.cos(angle),
        lastPoint.plotY + arrowWidth * Math.sin(angle),
        'Z'
     );
    } else {     

       if (arrowCheck === true) {

          pathTag = document.getElementById("arrow");
          if (pathTag != null) {
             pathTag.remove(pathTag);
          }
       }

       path.push(
         'L',
         lastPoint.plotX - arrowWidth * Math.cos(angle),
         lastPoint.plotY + arrowWidth * Math.sin(angle)
       );
       path.push(
         lastPoint.plotX - arrowLength * Math.sin(angle),
         lastPoint.plotY - arrowLength * Math.cos(angle)
       );
       path.push(
         lastPoint.plotX + arrowWidth * Math.cos(angle),
         lastPoint.plotY - arrowWidth * Math.sin(angle),
         'Z'
       );

    }

    series.chart.renderer.path(path)
    .attr({
       fill: series.color,
       id: 'arrow'
    })
    .add(series.group);

    arrowCheck = true;

   });
}(Highcharts));
  • Working example: http://jsfiddle/vyfsf1ft/3/

Hope this helps. Cheers!

Great help for me!

One note, the code above only works if there is only one series on the graph. I made a few changes that lets it work with more than one series. The issue was that since each arrow had the same ID, it would wipe out the prior arrows for other series, thinking they were old arrows that needed to be redrawn. All I did was change the id to be a bo of 'arrow' and the series name, so it only wipes out the prior arrows for the specific series.

(function(H) {

    var arrowCheck = false,
        pathTag;

    H.wrap(H.Series.prototype, 'drawGraph', function(proceed) {

      // Now apply the original function with the original arguments, 
      // which are sliced off this function's arguments
      proceed.apply(this, Array.prototype.slice.call(arguments, 1));

      // used a bit of regex to clean up the series name enough to be used as an id
      var arrowName = "arrow"+this.name.replace(/\W/g,"_").toLowerCase();
      // console.log("----------->arrowName:"+arrowName)

      var arrowLength = 15,
        arrowWidth = 7,
        series = this,
        data = series.data,
        len = data.length,
        segments = data,
        lastSeg = segments[segments.length - 1],
        path = [],
        lastPoint = null,
        nextLastPoint = null;

      if (lastSeg.y == 0) {
        lastPoint = segments[segments.length - 2];
        nextLastPoint = segments[segments.length - 1];
      } else {
        lastPoint = segments[segments.length - 1];
        nextLastPoint = segments[segments.length - 2];
      }

      var angle = Math.atan((lastPoint.plotX - nextLastPoint.plotX) /
        (lastPoint.plotY - nextLastPoint.plotY));

      if (angle < 0) angle = Math.PI + angle;

      path.push('M', lastPoint.plotX, lastPoint.plotY);

      if (lastPoint.plotX > nextLastPoint.plotX) {

        if (arrowCheck === true) {
          //replaced 'arrow' with arrowName
          pathTag = document.getElementById(arrowName);
          if (pathTag != null) {
            pathTag.remove(pathTag);
          }
        }

        path.push(
          'L',
          lastPoint.plotX + arrowWidth * Math.cos(angle),
          lastPoint.plotY - arrowWidth * Math.sin(angle)
        );
        path.push(
          lastPoint.plotX + arrowLength * Math.sin(angle),
          lastPoint.plotY + arrowLength * Math.cos(angle)
        );
        path.push(
          lastPoint.plotX - arrowWidth * Math.cos(angle),
          lastPoint.plotY + arrowWidth * Math.sin(angle),
          'Z'
        );
      } else {


        if (arrowCheck === true) {
          //replaced 'arrow' with arrowName
          pathTag = document.getElementById(arrowName);
          if (pathTag != null) {
            pathTag.remove(pathTag);
          }
        }

        path.push(
          'L',
          lastPoint.plotX - arrowWidth * Math.cos(angle),
          lastPoint.plotY + arrowWidth * Math.sin(angle)
        );
        path.push(
          lastPoint.plotX - arrowLength * Math.sin(angle),
          lastPoint.plotY - arrowLength * Math.cos(angle)
        );
        path.push(
          lastPoint.plotX + arrowWidth * Math.cos(angle),
          lastPoint.plotY - arrowWidth * Math.sin(angle),
          'Z'
        );
      }

      series.chart.renderer.path(path)
        .attr({
          fill: series.color,
          id: arrowName //changed from 'arrow' to arrowName to enable more than one series with an arrow
        })
        .add(series.group);

       arrowCheck = true;

    });
  }(Highcharts));

Linted /cleaned up version of jmfeland's answer with two series

http://jsfiddle/vyfsf1ft/18/

$(function() {

  (function(H) {
    var arrowCheck = false
    var pathTag

    H.wrap(H.Series.prototype, 'drawGraph', function(proceed) {
      // Apply the original function with the original arguments,
      // which are sliced off this function's arguments
      var series = this
      proceed.apply(series, Array.prototype.slice.call(arguments, 1))

      var arrowName = 'arrow' + series.name.replace(/\W/g, '_').toLowerCase()
      var arrowLength = 15
      var arrowWidth = 7
      var data = series.data
      var len = data.length
      var lastSeg = data[len - 1]
      var path = []
      var lastPoint = null
      var nextLastPoint = null

      if (lastSeg.y == 0) {
        lastPoint = data[len - 2]
        nextLastPoint = data[len - 1]
      } else {
        lastPoint = data[len - 1]
        nextLastPoint = data[len - 2]
      }

      var angle = Math.atan((lastPoint.plotX - nextLastPoint.plotX) /
        (lastPoint.plotY - nextLastPoint.plotY))

      if (angle < 0) angle = Math.PI + angle

      path.push('M', lastPoint.plotX, lastPoint.plotY)

      if (lastPoint.plotX > nextLastPoint.plotX) {
        if (arrowCheck === true) {
          pathTag = document.getElementById(arrowName)
          if (pathTag != null) {
            pathTag.remove(pathTag)
          }
        }

        path.push(
          'L',
          lastPoint.plotX + arrowWidth * Math.cos(angle),
          lastPoint.plotY - arrowWidth * Math.sin(angle)
        )
        path.push(
          lastPoint.plotX + arrowLength * Math.sin(angle),
          lastPoint.plotY + arrowLength * Math.cos(angle)
        )
        path.push(
          lastPoint.plotX - arrowWidth * Math.cos(angle),
          lastPoint.plotY + arrowWidth * Math.sin(angle),
          'Z'
        )
      } else {
        if (arrowCheck === true) {
          pathTag = document.getElementById(arrowName)
          if (pathTag != null) {
            pathTag.remove(pathTag)
          }
        }

        path.push(
          'L',
          lastPoint.plotX - arrowWidth * Math.cos(angle),
          lastPoint.plotY + arrowWidth * Math.sin(angle)
        )
        path.push(
          lastPoint.plotX - arrowLength * Math.sin(angle),
          lastPoint.plotY - arrowLength * Math.cos(angle)
        )
        path.push(
          lastPoint.plotX + arrowWidth * Math.cos(angle),
          lastPoint.plotY - arrowWidth * Math.sin(angle),
          'Z'
        )
      }

      series.chart.renderer.path(path)
        .attr({
          fill: series.color,
          id: arrowName,
        })
        .add(series.group)

      arrowCheck = true
    })
  }(Highcharts))

  var chart = new Highcharts.Chart({
    chart: {
      renderTo: 'container',
      defaultSeriesType: 'scatter'
    },
    xAxis: {
      min: 0,
      max: 10
    },
    plotOptions: {
      series: {
        animation: {
          duration: 2000
        },
        lineWidth: 2,
        marker: {
          enabled: false
        }
      },
      states: {
        hover: {
          enabled: true,
          lineWidth: 2
        },
      }
    },
    series: [{
      name: 'main',
      id: 'main',
      data: [
        [0, 0],
        [3, 4]
      ]
    }, {
      name: 'secondary',
      id: 'secondary',
      data: [
        [1, 0],
        [5, 8]
      ]
    }]
  })
})

(UPDATE) plot graph XY

if (lastPoint.plotX > nextLastPoint.plotX || (lastPoint.plotX==nextLastPoint.plotX && lastPoint.plotY > nextLastPoint.plotY)) 

Grap Arrow Line XY

本文标签: javascriptHow to draw arrows on a linechart with HighchartsStack Overflow