Dario Car Šagud
Dario Car Šagud

Reputation: 69

How to change a color of one particular bar using jqplot and stacked bar chart

I got one straight forward question. Is it possible to change the color of one bar in stacked bar chart in any way (using jqplot options or relying on a hack)?

I have this:

enter image description here

I want this:

enter image description here

So as you can already assume I am using 3 different colors for the stacked bar chart:

seriesColors: ['#afafaf', '#c4c6c4', '#dbdcdd']

Problem is that I want to add one specific color for 1 particular bar.

Here is the JS code:

$(document).ready(
    function() {

        var el = [ 3, 6, 0, 10, 12 ];
        var ael = [ 14, 5, 0, 4, 2 ];
        var ipv = [ 4, 9, 0, 8, 4 ];
        var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May' ];
        var colors = ['blue', 'red', 'white'];

        plot3 = $.jqplot('elDiagram', [ el, ael, ipv ], {
            stackSeries : true,
            seriesColors: colors,
            captureRightClick : true,
            seriesDefaults : {
                renderer : $.jqplot.BarRenderer,
                rendererOptions : {
                    barMargin : 30,
                    varyBarColor : true,
                    highlightMouseDown : true,
                    barWidth: 60
                },
                pointLabels : {
                    show : true
                }
            },
            axes : {
                xaxis : {
                    renderer : $.jqplot.CategoryAxisRenderer,
                    ticks : months,
                    tickOptions : {
                        mark : 'outside'
                    }
                },
                yaxis : {
                    tickOptions : {
                        show : false
                    },
                    padMin : 0
                }
            },
            series : [ {
                label : 'bla1'
            }, {
                label : 'bla2'
            }, {
                label : 'bla3'
            } ],
            legend : {
                show : true,
                location : 'ne',
                placement : 'inside'
            }
        });     
    });

Thanks!

Upvotes: 1

Views: 886

Answers (1)

Dario Car Šagud
Dario Car Šagud

Reputation: 69

OK, the solution was a hack which I will show and describe here:

  1. You need to overwrite the function called $.jqplot.BarRenderer.prototype.draw and change some of the lines
  2. You need to overwrite the function called getStart(sidx, didx, comp, plot, axis)
  3. You need to overwrite the function called $.jqplot.ShapeRenderer.prototype.draw and change some of the lines

1.:

$.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options, plot) {
   var i;
   // Ughhh, have to make a copy of options b/c it may be
   // modified later.
   var opts = $.extend({}, options);

   .................................
   <other code>
   .................................

   var clr = opts.fillStyle || this.color;
   this._dataColors.push(clr);
   this.renderer.shapeRenderer.draw(ctx, points, opts, i, pos); // changed line

I changed the line in that way that I added i and pos parameters into the function. The reason was to indicate the current bar and position in the bar.

2.:

function getStart(sidx, didx, comp, plot, axis) {
   // check if sign change
   var seriesIndex = sidx, prevSeriesIndex = sidx - 1, start, prevVal, aidx = (axis === 'x') ? 0 : 1;
   // is this not the first series?
   if (seriesIndex > 0) {
       prevVal = plot.series[prevSeriesIndex]._plotData[didx][aidx];
       // is there a sign change
       if ((comp * prevVal) < 0) {
           start = getStart(prevSeriesIndex, didx, comp, plot, axis);
       }
       // no sign change.
       else {
           start = plot.series[prevSeriesIndex].gridData[didx][aidx];
       }
   }
   // if first series, return value at 0
   else {
       start = (aidx === 0) ? plot.series[seriesIndex]._xaxis.series_u2p(0) : plot.series[seriesIndex]._yaxis.series_u2p(0);
   }
   return start;
}

Nothing is changed here. You just need to copy the function because your new overwritten function cannot use it from jQPlot library.

3.:

$.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options, currentBar, position) {
    ctx.save();
    var opts = (options != null) ? options : {};
    var fill = (opts.fill != null) ? opts.fill : this.fill;
    var closePath = (opts.closePath != null) ? opts.closePath : this.closePath;
    var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect;
    var strokeRect = (opts.strokeRect != null) ? opts.strokeRect
            : this.strokeRect;
    var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect;
    var isarc = (opts.isarc != null) ? opts.isarc : this.isarc;
    var linePattern = (opts.linePattern != null) ? opts.linePattern
            : this.linePattern;
    var ctxPattern = $.jqplot.LinePattern(ctx, linePattern);
    ctx.lineWidth = opts.lineWidth || this.lineWidth;
    ctx.lineJoin = opts.lineJoin || this.lineJoin;
    ctx.lineCap = opts.lineCap || this.lineCap;
    ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle;
    ctx.fillStyle = opts.fillStyle || this.fillStyle;
    if (currentBar == activeColumn && position == 0) { // adding different color for the specific bar
        ctx.fillStyle = defaultColors[0];
    } else if (currentBar == activeColumn && position == 1) {
        ctx.fillStyle = defaultColors[1];
    } else if (currentBar == activeColumn && position == 2) {
        ctx.fillStyle = defaultColors[2];
    }
    ctx.beginPath();
    if (isarc) {
        ctx.arc(points[0], points[1], points[2], points[3], points[4], true);
        if (closePath) {
            ctx.closePath();
        }
        if (fill) {
            ctx.fill();
        } else {
            ctx.stroke();
        }
        ctx.restore();
        return;
    } else if (clearRect) {
        ctx.clearRect(points[0], points[1], points[2], points[3]);
        ctx.restore();
        return;
    } else if (fillRect || strokeRect) {
        if (fillRect) {
            ctx.fillRect(points[0], points[1], points[2], points[3]);
        }
        if (strokeRect) {
            ctx.strokeRect(points[0], points[1], points[2], points[3]);
            ctx.restore();
            return;
        }
    } else if (points && points.length) {
        var move = true;
        for ( var i = 0; i < points.length; i++) {
            // skip to the first non-null point and move to it.
            if (points[i][0] != null && points[i][1] != null) {
                if (move) {
                    ctxPattern.moveTo(points[i][0], points[i][1]);
                    move = false;
                } else {
                    ctxPattern.lineTo(points[i][0], points[i][1]);
                }
            } else {
                move = true;
            }
        }
        if (closePath) {
            ctxPattern.closePath();
        }
        if (fill) {
            ctx.fill();
        } else {
            ctx.stroke();
        }
    }
    ctx.restore();
};

Here you need to check whether the bar your currently at, is the default one. The important part of the code is:

if (currentBar == activeColumn && position == 0) { // adding different color for the specific bar
    ctx.fillStyle = defaultColors[0];
} else if (currentBar == activeColumn && position == 1) {
    ctx.fillStyle = defaultColors[1];
} else if (currentBar == activeColumn && position == 2) {
    ctx.fillStyle = defaultColors[2];
}

I added 3 different colors for that bar just to have "more fancy" diagram :)

Upvotes: 1

Related Questions