如何在ChartJS中做圆形刻度线?

gpnt7bae  于 2023-05-06  发布在  Chart.js
关注(0)|答案(1)|浏览(201)

在ChartJS中有没有方法使X轴刻度线变圆?到目前为止,我所做的就是把它们变成破折号,但这不是我需要的。
第一个截图是我想要的样子,第二个是我到目前为止是如何做到的。我查阅了库文档,找不到任何参数负责刻度线的形状。也许有人知道一个方法,可以帮助我吗?

9rygscc1

9rygscc11#

可以通过扩展图表所使用的刻度类来绘制自定义刻度,如documentation中所述。特别地,要在线性轴上绘制圆形刻度,可以覆盖LinearScaledrawGrid方法:

class LinearScaleWithRoundTicks extends Chart.LinearScale{
    static id = "linear-roundTicks";
    drawGrid(chartArea) {
        const optionsGrid = this.options.grid,
            optionsTicks = this.options.ticks;
        let drawTicks = false;
        if(optionsGrid.display && optionsGrid.drawTicks){
            optionsGrid.drawTicks = false;
            drawTicks = true;
        }
        super.drawGrid(chartArea);
        if(drawTicks){
            optionsGrid.drawTicks = true;
            const ctx = this.ctx;
            const items = this._gridLineItems;

            for(const item of items){
                const tickR = optionsTicks.radius ?? 4,
                    fillColor = optionsTicks.backgroundColor ?? 'black';
                ctx.save();
                ctx.beginPath();
                ctx.arc(item.tx1, item.ty1, tickR, 0, 2*Math.PI);
                ctx.fillStyle = fillColor;
                ctx.fill();
                ctx.restore();
            }
        }
    }
}
Chart.register(LinearScaleWithRoundTicks);

如下面的示例中使用:

class LinearScaleWithRoundTicks extends Chart.LinearScale{
    static id = "linear-roundTicks";
    drawGrid(chartArea) {
        const optionsGrid = this.options.grid,
            optionsTicks = this.options.ticks;
        let drawTicks = false;
        if(optionsGrid.display && optionsGrid.drawTicks){
            optionsGrid.drawTicks = false;
            drawTicks = true;
        }
        super.drawGrid(chartArea);
        if(drawTicks){
            optionsGrid.drawTicks = true;
            const ctx = this.ctx;
            const items = this._gridLineItems;

            for(const item of items){
                const tickR = optionsTicks.radius ?? 4,
                    fillColor = optionsTicks.backgroundColor ?? 'black';
                ctx.save();
                ctx.beginPath();
                ctx.arc(item.tx1, item.ty1, tickR, 0, 2*Math.PI);
                ctx.fillStyle = fillColor;
                ctx.fill();
                ctx.restore();
            }
        }
    }
}
Chart.register(LinearScaleWithRoundTicks);

const nPoints = 15;
const datasets = Array.from({length: 2},
    (_, dataSet)=>({
        data: Array.from(
            {length: nPoints},
            (_, i)=>({
                x: i+1,
                y: 2+Math.cos((-i*4*(dataSet+1))/nPoints)+Math.random()/5
            })
        ),
        tension: 0.3,
        label: 'Set '+(dataSet+1),
        borderWidth: 1
    })
);
const config = {
    type: 'line',
    data: {datasets},
    options: {
        scales:{
            x: {
                type: 'linear-roundTicks',
                ticks:{
                    radius: 4,
                    backgroundColor: 'rgba(0,0,0,0.4)'
                },
            }
        },
        animation: false
    },
};
new Chart(document.querySelector('#myChart'), config);
<div style="min-height:400px">
<canvas id="myChart"></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>

这里有一个“mixin vs inheritance”问题:上述解决方案仅适用于线性标度;如果图表具有多种类型的轴(例如,类别、时间、线性),则必须扩展所使用的标度的所有类别。扩展基本情况Scale将不起作用,因为派生类(例如CategoryScale)使用原始类,而不是扩展的类。这就是为什么mixin是一种适用于任何类型的轴的解决方案,尽管看起来更黑客。
在下面的示例中,drawGrid在其beforeDataLimits回调中被更改(每个轴仅更改一次)。因此,对于每个轴,给出不同的绘图函数drawXTickdrawYTick,一个是线性的,另一个是分类的。

function mixIn_tickDraw(scale, drawTick){
    if(!scale.customTickDraw){
        scale.customTickDraw = true;
        const super_drawGrid = scale.drawGrid;
        scale.drawGrid = function(chartArea){
            const optionsGrid = this.options.grid,
                optionsTicks = this.options.ticks;
            let drawTicks = false;
            if(optionsGrid.display && optionsGrid.drawTicks){
                optionsGrid.drawTicks = false;
                drawTicks = true;
            }
            super_drawGrid.call(this, chartArea);
            if(drawTicks){
                optionsGrid.drawTicks = true;
                const ctx = this.ctx;
                const items = this._gridLineItems;

                for(const item of items){
                    drawTick(ctx, item, optionsTicks);
                }
            }
        }
    }
}

function drawXTick(ctx, tick, optionsTicks){
    const tickR = optionsTicks.radius ?? 4,
        fillColor = optionsTicks.backgroundColor ?? 'black';
    ctx.save();
    ctx.beginPath();
    ctx.arc(tick.tx1, tick.ty1, tickR, 0, 2 * Math.PI);
    ctx.fillStyle = fillColor;
    ctx.fill();
    ctx.restore();
}

function drawYTick(ctx, tick, optionsTicks){
    const tickR = optionsTicks.radius ?? 4,
        fillColor = optionsTicks.backgroundColor ?? 'gray';
    ctx.save();
    const nSpikes = 5;
    //draw star, from https://stackoverflow.com/a/58043598/16466946
    ctx.beginPath();
    for(let i = 0; i < nSpikes*2; i++){
        let rotation = Math.PI/2;
        let angle = (i/(nSpikes*2))*Math.PI*2+rotation;
        let dist = tickR * (1+ i%2);
        let x = tick.tx1 + Math.cos(angle)*dist;
        let y = tick.ty1 +Math.sin(angle)*dist;
        if(i === 0) {
            ctx.moveTo(x, y);
            continue; //skip
        }
        ctx.lineTo(x, y);
    }
    ctx.closePath();
    ctx.fillStyle = fillColor;
    ctx.fill();
    ctx.restore();
}

const nPoints = 15;
const datasets = Array.from({length: 2},
    (_, dataSet)=>({
        data: Array.from(
            {length: nPoints},
            (_, i)=> 2+Math.cos((-i*4*(dataSet+1))/nPoints)+Math.random()/5
        ),
        tension: 0.3,
        label: 'Set '+(dataSet+1),
        borderWidth: 1
    })
);
const config = {
    type: 'line',
    data: {
        labels: Array.from({length: nPoints}, (_, i)=>'L'+(i+1)),
        datasets
    },
    options: {
        scales:{
            x: {
                ticks:{
                    radius: 4,
                    backgroundColor: 'rgba(16,92,78,0.7)'
                },
                beforeDataLimits(scale){
                    mixIn_tickDraw(scale, drawXTick);
                }
            },
            y: {
                ticks:{
                    radius: 4,
                    backgroundColor: 'rgba(0,0,255,0.9)'
                },
                beforeDataLimits(scale){
                    mixIn_tickDraw(scale, drawYTick);
                }
            }
        },
        //animation: false
    },
};

new Chart(document.querySelector('#myChart'), config);
<div style="min-height:400px">
<canvas id="myChart"></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>

相关问题