Label rotation #15

Open
wants to merge 7 commits into
from

Projects

None yet
@paradoxxxzero

Hi! I've implemented the x axis label rotation, with the labelAngle option.

Very useful for time graphs with long date labels.

This fix : http://code.google.com/p/flot/issues/detail?id=85

@heyi
heyi commented on 90dc901 Nov 11, 2011

I was happy that much worked.
thank you very much .

Owner

There's still some cropping issues but i'm happy you found this usefull

ukch commented Nov 24, 2011

I've noticed it's still not possible to have ie. vertical right-facing text (as in, labelAngle=90 and labelAngle=270 do the same thing).

sievlev commented Dec 6, 2011

Looks that it doesn't work with negative angles

egonpin commented Feb 24, 2012

please examples of this functionality

Label angle of 285 works pretty well for 39b6199. Here's an example from RAGE:
Example from RAGE

arski commented Mar 12, 2012

works perfectly for me for positive angles, but the labels shift to the left a bit with negative angles o_O I would upload a screenshot if I could figure out how :/

@arski You can fork flot, upload your image in your fork's download section, get its URL, then use this URL with GitHub's Markdown.

arski commented Mar 13, 2012

right.. could it be more complicated :D anyway, the labels are like 12px to the left instead of being right below the grid lines when using rotation -90 (or any negative). I fixed it now in the flot.js directly by adding 12px to the position, but that seems ugly..

Also, what happened to the tickLabel CSS class - it used to be wrapping the labels so the fix would have been an easy CSS adjust.. but those seem to be gone now :(

Bump! BTW who's responsible for merging (and not merging) these requests?

This was referenced Sep 28, 2012
qmx commented Oct 14, 2012

I've just rebased against master here => https://github.com/qmx/flot/tree/with_label_rotation

Why hasn't this this pull request been merged yet? It seems like a very useful addition to flot.js and doesn't seem to have any apparent issues, a clarification would be appreciated

Owner
dnschnur commented Nov 5, 2012

It hasn't been merged because the implementation is still buggy, somewhat complicated, and requires canvas text support. We're finalizing proper library-wide support for canvas text in 0.8; I'm not considering any pull requests that require it until after that release.

qmx commented Nov 5, 2012

yeah, it has bugs, I've just rebased to keep the discussion alive, and I'm using it while the better solution doesn't arrive

irbian commented Dec 21, 2012

In case someone wants another point of view, I solved the problem spliting the labels into lines with this function (adapted from here (http://stackoverflow.com/questions/11298106/break-a-string-in-substrings-lines-without-split-words)).

function splitIntoLines(text, maxLineLength, mode) {
    maxLineLength = typeof maxLineLength !== 'undefined' ? maxLineLength : 15;
    mode        = typeof mode !== 'undefined' ? mode : "array";
    var words   = text.split(/\s+/);
    var output  = [];
    console.log(words);
    var buffer  = '';
    for (var i = 0; i < words.length; i++) {

        var word = words[i];
        console.log(word);
        // if buffer has something, add a space
        console.log(buffer);
        if (buffer.length) {
            if (buffer.length + word.length + 1 < maxLineLength) {
                buffer += ' ' + word;
            } else {
                if (mode == "array") {
                    output.push(buffer);
                } else if (mode == "string") {
                    output += buffer + "\n";
                } else {
                    output += "ERROR";
                }
                buffer = word;
            }

        // otherwise, it's the first word
        } else {
            if (buffer.length + word.length < maxLineLength) {
                buffer += word;
            } else {
                output.push(buffer);
                buffer = word;
            }
        }
    }
    if (buffer.length) {
        if (mode == "array") {
                    output.push(buffer);
                } else if (mode == "string") {
                    output += buffer + "\n";
                } else {
                    output += "ERROR";
        }
    }
    console.log(output);
    return output;
}

So, what about this pull request now ? Do you plan to introduce such a feature in a future release of flot library ?

Owner

Yes; the main reason so much work was done on the text API in 0.8 was to make it easy to implement features such as this one in later releases.

bsdis commented May 16, 2013

is there any possibility this will incorporated soon?
It seems 0.8 has already been released and then it should be rather easy to implement no?

Owner

We're adding this feature in 0.9, but that's still a little ways off. The implementation will be completely different, so it's not just a matter of merging this pull request.

ova2 commented Aug 13, 2013

Hi. What is the state of this issue? Is there already an implementation in 0.9-SNAPSHOT?

Owner

Not yet, but it's the next thing we're working on for 0.9.

ova2 commented Aug 14, 2013

Hi David,

I spent one day and implemented rotated ticks for any single or multiple X / Y axes. Below is the implementation for labels rendered as HTML (not canvas). An angle can be set for any axis with the option tickAngle in degree. Feel free to take the idea for your implementation. I'm sure you can do this better.

rotateTickLabels: function(plot) {
    var axes = plot.getAxes();
    var deg2radians = Math.PI / 180;
    var tickAngle, rotateAngle, cos, sin, tickLabels, length, $tickLabel, height, width,
        left, top, x1, x2, x3, minX, maxX, y1, y2, y3, minY, maxY;

    var isIE = !$.support.leadingWhitespace;

    // rotate labels on X axes if needed
    var i = 1;
    var curXaxis = axes.xaxis;

    while (curXaxis && $.isNumeric(curXaxis.options.tickAngle)) {
        tickAngle = curXaxis.options.tickAngle;
        rotateAngle = tickAngle * deg2radians;
        cos = Math.cos(rotateAngle);
        sin = Math.sin(rotateAngle);

        tickLabels = plot.getPlaceholder().find(".x" + i + "Axis > .tickLabel");            
        length = tickLabels.length;

        for (var j=0; j<length; j++) {
            $tickLabel = $(tickLabels[j]);

            // original height / width of the div with text
            height = $tickLabel.outerHeight();
            width = $tickLabel.outerWidth();

            if (isIE) {
                // IE8 and below                    
                // width after rotation
                // http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated
                // (w,0) rotation
                x1 = cos * width;                    
                // (0,h) rotation
                x2 = -sin * height;                    
                // (w,h) rotation
                x3 = cos * width - sin * height;

                minX = Math.min(0, x1, x2, x3);
                maxX = Math.max(0, x1, x2, x3);

                // diff to the left
                left = parseInt($tickLabel.css("left"), 10);
                left = isNaN(left) ? 0 : left;

                // rotate
                $tickLabel.css({
                    "filter": "progid:DXImageTransform.Microsoft.Matrix(M11="+cos+",M12="+(-sin)+",M21="+sin+",M22="+cos+",SizingMethod='auto expand')",
                    "zoom": 1,
                    "background-color": "white",
                    "left": left + Math.abs((maxX - minX - width) / 2) + "px" // adjust left position 
                });
            } else {
                // modern browsers                   
                // rotate
                $tickLabel.css({
                    "-webkit-transform": "rotate(" + tickAngle + "deg)",
                    "-moz-transform": "rotate(" + tickAngle + "deg)",
                    "-ms-transform": "rotate(" + tickAngle + "deg)",
                    "-o-transform": "rotate(" + tickAngle + "deg)",
                    "transform": "rotate(" + tickAngle + "deg)"
                });

                // height after rotation
                // http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated
                // (w,0) rotation
                y1 = sin * width;                    
                // (0,h) rotation
                y2 = cos * height;                    
                // (w,h) rotation
                y3 = sin * width + cos * height;

                minY = Math.min(0, y1, y2, y3);
                maxY = Math.max(0, y1, y2, y3);

                // diff to the top
                top = parseInt($tickLabel.css("top"), 10);
                top = isNaN(top) ? 0 : top;

                // adjust top position
                $tickLabel.css("top", top + ((maxY - minY - height) / 2) + "px");
            }
        }

        i++;
        curXaxis = axes['x' + i + 'axis'];
    }

    // rotate labels on Y axes if needed
    i = 1;
    var curYaxis = axes.yaxis;

    while (curYaxis && $.isNumeric(curYaxis.options.tickAngle)) {
        tickAngle = curYaxis.options.tickAngle;
        rotateAngle = tickAngle * deg2radians;
        cos = Math.cos(rotateAngle);
        sin = Math.sin(rotateAngle);

        tickLabels = plot.getPlaceholder().find(".y" + i + "Axis > .tickLabel");
        length = tickLabels.length;

        for (var k=0; k<length; k++) {
            $tickLabel = $(tickLabels[k]);

            // original height / width of the div with text
            height = $tickLabel.outerHeight();
            width = $tickLabel.outerWidth();

            if (isIE) {
                // IE8 and below
                // height after rotation
                // http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated
                // (w,0) rotation
                y1 = sin * width;                    
                // (0,h) rotation
                y2 = cos * height;                    
                // (w,h) rotation
                y3 = sin * width + cos * height;

                minY = Math.min(0, y1, y2, y3);
                maxY = Math.max(0, y1, y2, y3);

                // diff to the top
                top = parseInt($tickLabel.css("top"), 10);
                top = isNaN(top) ? 0 : top;

                // rotate
                $tickLabel.css({
                    "filter": "progid:DXImageTransform.Microsoft.Matrix(M11="+cos+",M12="+(-sin)+",M21="+sin+",M22="+cos+",SizingMethod='auto expand')",
                    "zoom": 1,
                    "background-color": "white",
                    "top": top - Math.abs((maxY - minY - width) / 2) + "px" // adjust top position
                });
            } else {
                // modern browsers                       
                // rotate
                $tickLabel.css({
                    "-webkit-transform": "rotate(" + tickAngle + "deg)",
                    "-moz-transform": "rotate(" + tickAngle + "deg)",
                    "-ms-transform": "rotate(" + tickAngle + "deg)",
                    "-o-transform": "rotate(" + tickAngle + "deg)",
                    "transform": "rotate(" + tickAngle + "deg)"
                });

                // width after rotation
                // http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated
                // (w,0) rotation
                x1 = cos * width;                    
                // (0,h) rotation
                x2 = -sin * height;                    
                // (w,h) rotation
                x3 = cos * width - sin * height;

                minX = Math.min(0, x1, x2, x3);
                maxX = Math.max(0, x1, x2, x3);

                // diff to the left
                left = parseInt($tickLabel.css("left"), 10);
                left = isNaN(left) ? 0 : left;

                // adjust left position
                $tickLabel.css("left", left - ((maxX - minX - width) / 2) + "px");
            }
        }

        i++;
        curYaxis = axes['y' + i + 'axis'];
    }
}

The rotation 90, 180, 270 degrees or -90, -180, -270 degrees look very well in all browsers. Unfortunately, the rotation for angles different to the listed above looks not good in Firefox / Chrome (blurry labels). Font increasing improves the look-and-feel of labels. Below is a link to three pictures. Rotation is -45 deg. Font-size is standard. The first one was made in FF (not sharp labels), the second one in IE8 and the last one in IE9.

http://imgur.com/a/pOyCH

As you see IE rocks :-).

ova2 commented Aug 15, 2013

Interesting, the quality of rotated labels was only blurry on my Ubuntu laptot. It is ok under Windows (good quality with any font-size). It seems to depend on hardware because CSS transform is used.

@pombredanne pombredanne pushed a commit to pombredanne/recline that referenced this pull request Mar 1, 2014
@rufuspollock rufuspollock [#58,flot-graph][s]: switch to horizontal bar chart as normal bar cha…
…rt tick labels are borked without rotation when lots of bars (rotation not yet in flot core [1]).

[1]: flot/flot#15
d1445ee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment