chart.js

how can I make a custom point style in ChartJS


In ChartJS one can change the point style to some predefined forms. But I need the point style to be an arrow so I tried to customize the point style but didn't succeed. I tried the following:

customTrianglePointStyle: function (ctx) {
const path = new Path2D();
const radius = 10;
path.moveTo(0, -radius);
path.lineTo(radius, radius);
path.lineTo(-radius, radius);
path.closePath();
return path;

},

and add it to the dataset

let dataset = {
data: JSON.parse(jsonData),
yAxisID: "y",
showLine: false,
pointRadius: 5,
pointStyle: this.customTrianglePointStyle,

}

But it falls back to the default point style.

In the documentation I found:

When a string is provided, the following values are supported: 'circle' 'cross' 'crossRot' 'dash' 'line' 'rect' 'rectRounded' 'rectRot' 'star' 'triangle' false If the value is an image or a canvas element, that image or canvas element is drawn on the canvas using drawImage

But in my case I don't want to draw an image but a shape on the canvas at each plot point.

Has someone an idea how I could achieve my goal.


Solution

  • As C3roe explained in the comments, and as is also stated in the documentation page you quoted from, you may provide the pointStyle as an HTMLCanvasElement (or an ImageElement). So, to set a custom style drawn using a canvas, you have to create a new canvas element, draw on that canvas using its RenderingContext and then 3) return the canvas (not the path).

    function pointStyle(){
        const canvas = document.createElement('canvas');
        canvas.height = 20;
        canvas.width = 20;
        const ctx = canvas.getContext('2d');
        const path = new Path2D();
        const radius = 10;
        path.moveTo(radius, 0);
        path.lineTo(2*radius, 2*radius);
        path.lineTo(0, 2*radius);
        path.closePath();
        ctx.strokeStyle = '#000';
        ctx.fillStyle = 'red';
        ctx.stroke(path);
        ctx.fill(path);
        return canvas;
    }
    

    The point style may be customised for each point, through the first argument received by the pointStyle function, that contains all information regarding the data point (explore it with console.log). You may use that information to rotate the points of the Path2D around the center of the canvas.

    However, we have a built-in point property for rotation, pointRotation, which is also scriptable receiving the same arguments as pointStyle (note that you may also use it to rotate a standard point style, like a triangle).

    Assuming the rotation information in the data is given as a rotate entry for each data point (e.g., {x: 0, y: 25, rotate: 20}) that contains the rotation of the point in degrees, the pointRotation function can be:

    function pointRotation(dataContext){
        return dataContext.raw?.rotate ?? 0;
    }
    

    which is much simpler than implementing rotation at the pointStyle level.

    Demo stack snippet:

    const dataXY = [
        {x: 0, y: 25, rotate: 20},
        {x: 100, y: 42, rotate: 80},
        {x: 200, y: 35, rotate: 120}
    ];
    
    function pointStyle(dataContext){
        // dataContext may be used to customize the image
        const canvas = document.createElement('canvas');
        canvas.height = 20;
        canvas.width = 20;
        const ctx = canvas.getContext('2d');
        const path = new Path2D();
        const radius = 10;
        path.moveTo(radius, 0);
        path.lineTo(2*radius, 2*radius);
        path.lineTo(0, 2*radius);
        path.closePath();
        ctx.strokeStyle = '#000';
        ctx.fillStyle = 'red';
        ctx.stroke(path);
        ctx.fill(path);
        return canvas;
    }
    
    function pointRotation(dataContext){
        return dataContext.raw?.rotate ?? 0;
    }
    
    new Chart('myChart', {
        type: 'line',
        data: {
            datasets: [{
                label: 'Dataset 1',
                data: dataXY
            }]
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            pointStyle: pointStyle,
            pointRotation: pointRotation,
            scales: {
                x: {
                    type: 'linear',
                    min: -5,
                    max: 205,
                    ticks: {
                        includeBounds: false,
                    },
                },
                y:{
                    grace: 2
                }
            }
        }
    });
    <div style="min-height: 250px">
        <canvas id="myChart"></canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>