How to get angled labels that follow your line

A guide for creating a Line chart which has angled labels that follow the shape of the line. These labels are added to the canvas after the line has been drawn (using the ondraw event) with some custom code.

[No canvas support]

This is a demonstration that shows you how to add angled labels to your line where these labels follow the undulations of the line.

These labels are added using the ondraw event and the API functions are used to help with the trigonometry that's involved in calculating the necessary angle.

The text is added using native canvas text (the accessible option is given to the RGraph.text2() function) so it can't be selected by the mouse (for cut/copy/paste functions).

The other text on the canvas is still accessible (but the pointer-events CSS option is set to none)

The code to achieve the chart is shown and commented below.

<script>
    // Define the data, labels, the ingraph labels and their size
    var data         = [1,9,5,6,3,4,8,5,2,2],
        labels       = ['Bob','Fred','John','Luis','Jo','Kane','Lou','Pob','Angie', 'Jim'],
        ingraph      = {up:'Good',level:'OK',down:'Bad',size:12};

    // Create the Line chart and add some configuration
    var line = new RGraph.Line({
        id: 'cvs',
        data: data,
        options: {
            shadow: false,
            noxaxis: true,
            backgroundGridVlines: false,
            backgroundGridBorder: false,
            labels: labels,
            ticksize: 7,
            hmargin: 15,
            textSize: 20,
            gutterLeft:  50,
            title: 'A chart with undulating labels',
            tickmarks: 'circle'
        }
    
    // Use the ondraw event to add the angular labels
    }).on('draw', function (obj)
    {
        // This is the first lines coordinates
        var coords = obj.coords;

        // Loop thru the coordinates for the line (but not the last one)
        for (var i=0; i<(coords.length) - 1; ++i) {
            
            // Work out the horizontal and vertical distance to the next point
            var dx = (coords[i + 1][0] - coords[i][0]) / 2,
                dy = (coords[i + 1][1] - coords[i][1]) / 2;
            
            // Work out the direction that the line is going so that the correct
            // label can be used
            if (coords[i + 1][1] < coords[i][1]) {
                var direction = 'up';
            } else if (coords[i + 1][1] > coords[i][1]) {
                var direction = 'down';
            } else {
                var direction = 'level';
            }
            
            // Work out the angle that the text should be drawn at
            var angle = RGraph.getAngleByXY(
                coords[i][0],
                coords[i][1],
                coords[i + 1][0],
                coords[i + 1][1],
            );

            // Use the API function to add the text to the chart
            RGraph.text2(obj, {
                x: coords[i][0] + dx,
                y: coords[i][1] + dy - 5,
                text: ingraph[direction],
                halign: 'center',
                valign: 'bottom',
                angle: angle * (180 / Math.PI),
                accessible: false,
                size: ingraph.size
            });
        }
    }).draw();
</script>

 

A version of the chart that uses the adjustable feature

[No canvas support]

This is an adjustable version of the above chart. You can add adjusting by simply adding two properties to the chart configuration - adjustable and ymax. The adjustable property adds the adjusting feature and the ymax property stops the scale from changing (when you move all of the points downwards the top value of the scale will change accordingly).


<script>
    // Define the data, labels, the ingraph labels and their size
    var data         = [1,9,5,6,3,4,8,5,2,2],
        labels       = ['Bob','Fred','John','Luis','Jo','Kane','Lou','Pob','Angie', 'Jim'],
        ingraph      = {up:'Good',level:'OK',down:'Bad',size:12};

    // Create the Line chart and add some configuration
    var line = new RGraph.Line({
        id: 'cvs2',
        data: data,
        options: {
            shadow: false,
            noxaxis: true,
            backgroundGridVlines: false,
            backgroundGridBorder: false,
            labels: labels,
            ticksize: 7,
            hmargin: 15,
            textSize: 20,
            gutterLeft:  50,
            title: 'An adjustable version of the chart',
            tickmarks: 'circle',
            adjustable: true // Add the adjusting feature,
            ymax: 10
        }
    
    // Use the ondraw event to add the angular labels
    }).on('draw', function (obj)
    {
        // This is the first lines coordinates
        var coords = obj.coords;

        // Loop thru the coordinates for the line (but not the last one)
        for (var i=0; i<(coords.length) - 1; ++i) {
            
            // Work out the horizontal and vertical distance to the next point
            var dx = (coords[i + 1][0] - coords[i][0]) / 2,
                dy = (coords[i + 1][1] - coords[i][1]) / 2;
            
            // Work out the direction that the line is going so that the correct
            // label can be used
            if (coords[i + 1][1] < coords[i][1]) {
                var direction = 'up';
            } else if (coords[i + 1][1] > coords[i][1]) {
                var direction = 'down';
            } else {
                var direction = 'level';
            }
            
            // Work out the angle that the text should be drawn at
            var angle = RGraph.getAngleByXY(
                coords[i][0],
                coords[i][1],
                coords[i + 1][0],
                coords[i + 1][1],
            );

            // Use the API function to add the text to the chart
            RGraph.text2(obj, {
                x: coords[i][0] + dx,
                y: coords[i][1] + dy - 5,
                text: ingraph[direction],
                halign: 'center',
                valign: 'bottom',
                angle: angle * (180 / Math.PI),
                accessible: false,
                size: ingraph.size
            });
        }
    }).draw();
</script>