Chartist-js: How do I just draw a single line at some value?

Created on 23 Oct 2015  路  18Comments  路  Source: gionkunz/chartist-js

I've messed with it for awhile and I can't seem to figure it out. I know how to draw random vector graphics and I know how to catch certain labels so I could potentially draw a line at a specific _labeled_ point, but I need to set "goal" lines. Let's say a specific user's goal is "121" but the chart I want labeled in 10s (10, 20, 30, etc) how can I draw a line where 121 would be even tho its above 120 and below the 130 label?

duplicate

Most helpful comment

Also, just for other people searching, I made a plugin for this original target line issue:

/**
 * Chartist.js plugin to display a "target" or "goal" line across the chart.
 * Only tested with bar charts. Works for horizontal and vertical bars.
 */
(function(window, document, Chartist) {
  'use strict';

  var defaultOptions = {
    // The class name so you can style the text
    className: 'ct-target-line',
    // The axis to draw the line. y == vertical bars, x == horizontal
    axis: 'y',
    // What value the target line should be drawn at
    value: null
  };

  Chartist.plugins = Chartist.plugins || {};

  Chartist.plugins.ctTargetLine = function(options) {
    options = Chartist.extend({}, defaultOptions, options);
    return function ctTargetLine (chart) {

      chart.on('created', function(context) {
        var projectTarget = {
          y: function (chartRect, bounds, value) {
            var targetLineY = chartRect.y1 - (chartRect.height() / bounds.max * value);

            return {
              x1: chartRect.x1,
              x2: chartRect.x2,
              y1: targetLineY,
              y2: targetLineY
            }
          },
          x: function (chartRect, bounds, value) {
            var targetLineX = chartRect.x1 + (chartRect.width() / bounds.max * value);

            return {
              x1: targetLineX,
              x2: targetLineX,
              y1: chartRect.y1,
              y2: chartRect.y2
            }
          }
        };

        var targetLine = projectTarget[options.axis](context.chartRect, context.bounds, options.value)

        context.svg.elem('line', targetLine, options.className);
      });
    }
  }

}(window, document, Chartist));

All 18 comments

Made an example. In this case I want to draw a line at "17" although it goes by 2s

Hi there. This is somewhat a duplicate of https://github.com/gionkunz/chartist-js/issues/235 . Please tell me if this is helpful.

Cheers
Gion

Hm, I don't think that code works? I tried it on my own with it set to 14 and it's not right:

image

I then double checked your example you linked to and that's actually wrong too. You set it to 4.5 but it's way off from 4.5:

js bin - collaborative javascript debugging 2015-10-24 18-18-17

I've been messing with these numbers and I can't seem to find an equation that works :\

I think I was over complicating it. If I change your projectY equation to this it seems to work:

var targetLineY = chartRect.y1 - (chartRect.height() / bounds.max * value)

Basically, on the right, it figures out how many pixels each tick must be by dividing the height by the max number of ticks. Then multiplies the total ticks by the value passed to it.

@OscarGodson how did you enable the values on the bars? 19, 15, 9, 13 etc ?

@shlomogoldstein I made a plugin for it. It was for a quick client site so it hasn't had extensive testing and no unit tests but this worked for us :)

/**
 * Chartist.js plugin to display a data label inside the bars in a Bar Chart
 */
(function(window, document, Chartist) {
  'use strict';
  var defaultOptions = {
    // The class name so you can style the text
    labelClass: 'ct-bar-label',

    // Use this to get the text of the data and you can return your own
    // formatted text. For example, for a percentage: 
    // {
    //  labelInterpolationFnc: function (text) { return text + '%' }
    // }
    labelInterpolationFnc: Chartist.noop,

    // Depending on your font size you may need to tweak these
    labelOffset: {
      x: 0,
      y: 0
    },

    // If labelOffset doesn't work for you and you need more custom positioning
    // you can use this. You can set position.x and position.y to functions and
    // instead of centering + labelOffset. This will _completely_ override the
    // built in positioning so labelOffset will no longer do anything. It will
    // pass the bar `data` back as the first param.
    //
    // Example:
    // Chartist.plugins.ctBarLabels({
    //   position: {
    //     x: function (data) {
    //       return data.x1 + 50; // align left with 50px of padding
    //     }
    //   }
    // });
    position: {
      x: null,
      y: null
    }
  };

  Chartist.plugins = Chartist.plugins || {};
  Chartist.plugins.ctBarLabels = function(options) {

    options = Chartist.extend({}, defaultOptions, options);

    var positionX = options.position.x || function (data) {
      return ((data.x1 + data.x2) / 2) + options.labelOffset.x;
    };

    var positionY = options.position.y || function (data) {
      return ((data.y1 + data.y2) / 2) + options.labelOffset.y;
    };

    return function ctBarLabels(chart) {
      // Since it's specific to bars, verify its a bar chart
      if(chart instanceof Chartist.Bar) {
        chart.on('draw', function(data) {
          // If the data we're drawing is the actual bar, let's add the text
          // inside of it
          if(data.type === 'bar') {
            data.group.elem('text', {
              // This gets the middle point of the bars and then adds the
              // optional offset to them
              x: positionX(data),
              y: positionY(data),
              style: 'text-anchor: middle'
            }, options.labelClass)
              .text(
              options.labelInterpolationFnc(
                // If there's not x (horizontal bars) there must be a y
                data.value.x || data.value.y
              )
            );
          }
        });
      }
    };
  };

}(window, document, Chartist));

Also, just for other people searching, I made a plugin for this original target line issue:

/**
 * Chartist.js plugin to display a "target" or "goal" line across the chart.
 * Only tested with bar charts. Works for horizontal and vertical bars.
 */
(function(window, document, Chartist) {
  'use strict';

  var defaultOptions = {
    // The class name so you can style the text
    className: 'ct-target-line',
    // The axis to draw the line. y == vertical bars, x == horizontal
    axis: 'y',
    // What value the target line should be drawn at
    value: null
  };

  Chartist.plugins = Chartist.plugins || {};

  Chartist.plugins.ctTargetLine = function(options) {
    options = Chartist.extend({}, defaultOptions, options);
    return function ctTargetLine (chart) {

      chart.on('created', function(context) {
        var projectTarget = {
          y: function (chartRect, bounds, value) {
            var targetLineY = chartRect.y1 - (chartRect.height() / bounds.max * value);

            return {
              x1: chartRect.x1,
              x2: chartRect.x2,
              y1: targetLineY,
              y2: targetLineY
            }
          },
          x: function (chartRect, bounds, value) {
            var targetLineX = chartRect.x1 + (chartRect.width() / bounds.max * value);

            return {
              x1: targetLineX,
              x2: targetLineX,
              y1: chartRect.y1,
              y2: chartRect.y2
            }
          }
        };

        var targetLine = projectTarget[options.axis](context.chartRect, context.bounds, options.value)

        context.svg.elem('line', targetLine, options.className);
      });
    }
  }

}(window, document, Chartist));

@OscarGodson thank you so much, it works perfectly!!!
Another question: how to style the text color? Using "stroke" and "stroke-width" is the only properties working for me. But the font looks blurry. How did you style it white?

try fill. Here's all my CSS for the bar label text above:

.ct-bar-label {
  font-size: 20px;
  font-weight: bold;
  fill: #fff;
}

@OscarGodson I'm getting a strange result with your ctTargetLine plugin as posted above. It seems to be calculating the y-position of the line incorrectly. I'm attempting to draw a line at 1.25 on the graph below and it's showing up around 1.6:

screen shot 2015-12-30 at 10 42 10 pm

Here's the relevant snippet from my code, you can see I'm passing a value option that's set to 1.25. Not sure where things are going wrong. I'm I missing something or is this a bug?

new Chartist.Line('#history', {
    labels: labels,
    series: [ratingsSeries]
  }, {
    fullWidth: true,
    height: '150px',
    width : (Game.show.episodes.length+1) * 200 + "px",
    lineSmooth: Chartist.Interpolation.none(),

    plugins: [
      Chartist.plugins.ctThreshold({
        threshold:1.25
      }),
      Chartist.plugins.ctTargetLine({
        value: 1.25
      })
    ]
  });
}    

Looking back at your original math I don't see how you can get away without using bounds.min. What about graphs (like mine) where the y-axis doesn't start at 0? How can that possibly be right?

After some experimentation, here's what ended up working for me:

var targetLineY = chartRect.padding.top + chartRect.height() *  Math.abs(bounds.max - value) / bounds.range ;

I wonder if the issue is that I'm using this for a line chart. You comments in the code say you'd only tested it with a bar chart. As I experimented with my chart I noticed that starting at chartRect.padding.top gave me the top of the graph. Then adding chartRect.height() to that gave me the graph's baseline. So then all I had left to do was scale the distance from the max value to the actual value (we're moving down from the top) to the range of the values in the graph. Not sure how robust this is for other types of graphs but it worked for me for my line graph.

Ah yes @atduskgreg, someone actually sent a PR to fix this here: https://github.com/yorkshireinteractive/chartist-goal-line/pull/8

I need to merge it but you're correct that you need the min val

Interesting. That does basically the same calculation as I was doing but doesn't use the padding. I think it will still be off without the padding...

@atduskgreg I can add it to my code, but you can also send a PR with your updated equation if you want :)

Hi all!

I'm using the target line in a line chart in order to enhance one of the grid lines. Is there a way to NOT place it on top of the data series lines, but behind them?

@allazis use a CSS selector to set the z-index to a value between the z-index of the svg element and z-index of the chart lines.

I fixed the plugin and made a pull request.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexcarpenter picture alexcarpenter  路  3Comments

alberk8 picture alberk8  路  4Comments

imkevinabraham picture imkevinabraham  路  3Comments

ShlomoRosenheimer picture ShlomoRosenheimer  路  3Comments

FabienPapet picture FabienPapet  路  4Comments