[Chartjs]-How to create Black Border for Every Y Scale's X Axis with Line Chart in Chartjs

1๐Ÿ‘

โœ…

A safe solution is to use the plugin chartjs-plugin-annotation that has a line annotation.

A line at the bottom of the axis y2 can be obtained by using the options:

options:{
    // other options .....
    plugins: {
        // other plugins .... 
        annotation: {
            annotations: {
                line_y2_bottom: {
                    type: 'line',
                    scaleID: 'y2',
                    value: function({chart}, props){
                        const scale = chart.scales[props.scaleID];
                        return scale.getValueForPixel(scale.bottom - 1) || scale.min;
                    },
                    borderColor: 'black',
                    borderWidth: 3,
                },
            }
        }
    }
}

If known apriori, the value can be set to a fixed numeric value. However, the fact that value, as well as most other properties, is scriptable is a great advantage in this context, when the axes have offset: true, so the actual minimal value (as well as maximal one) are not dictated exclusively by the data, but also by the objective of making all axes fit in the available space.

The line id, in the example above, line_y2_bottom is arbitrary, I chose it to be meaningful for humans, the only
requirement is that each annotation has a different id.

A minimal snippet example that draws a line at the bottom of each chart:

const x = Array.from({length: 101}, (_, i)=>({x: i/100}));
const data = {
    datasets: [
        {
            data: x.map(({x})=>({x, y: Math.exp(x+1)})),
            borderColor: 'red',
            yAxisID: 'y1'
        },
        {
            data: x.map(({x})=>({x, y: Math.exp(-x+1)})),
            borderColor: 'blue',
            yAxisID: 'y2',
        },
        {
            data: x.map(({x})=>({x, y: Math.sin(x+1)})),
            borderColor: 'green',
            yAxisID: 'y3',
        }
    ]
};
const config = {
    type: 'line',
    data: data,
    options: {
        responsive: true,
        aspectRatio: 1,
        plugins: {
            legend: {
                display: false,
            },
            annotation: {
                annotations: Object.fromEntries(['y1', 'y2', 'y3'].map(
                    scaleID => [
                        `line_${scaleID}_bottom`,
                        {
                            type: 'line',
                            scaleID,
                            value: function({chart}, props){
                                const scale = chart.scales[props.scaleID];
                                return scale.getValueForPixel(scale.bottom-1) || scale.min;
                            },
                            borderColor: 'black',
                            borderWidth: 3,
                        }
                    ])
                )
            }
        },

        scales: {
            x: {
                type: 'linear',
            },
            y1: {
                stack: 'demo',
                offset: true,
                stackWeight: 1,
            },
            y2: {
                stack: 'demo',
                stackWeight: 2,
                border:{
                    color: 'black',
                    width: 2
                },
                offset: true
            },
            y3: {
                stack: 'demo',
                offset: true,
                stackWeight: 1
            }
        }
    },
};
new Chart(document.querySelector('#chart1'), config);
<div style="width: 95vw">
   <canvas id="chart1"></canvas>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.0/chart.umd.js" integrity="sha512-CMF3tQtjOoOJoOKlsS7/2loJlkyctwzSoDK/S40iAB+MqWSaf50uObGQSk5Ny/gfRhRCjNLvoxuCvdnERU4WGg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/3.0.1/chartjs-plugin-annotation.min.js" integrity="sha512-Hn1w6YiiFw6p6S2lXv6yKeqTk0PLVzeCwWY9n32beuPjQ5HLcvz5l2QsP+KilEr1ws37rCTw3bZpvfvVIeTh0Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Leave a comment