Chartjs v4饼图,径向位移(偏移量)

问题描述 投票:0回答:1

基本上我正在尝试实现与这个问题完全相同但在 V4 中: Chartjs饼图,径向位移(偏移量)_

此代码答案在 v2.6 中有效,任何人都可以指导我使用 v4 的格式吗。

Chart.defaults.cutOutPie = Chart.helpers.clone(Chart.defaults.pie);

Chart.controllers.cutOutPie = Chart.controllers.pie.extend({
    updateElement: function(arc, index, reset) {
        Chart.controllers.pie.prototype.updateElement.call(this, arc, index, reset);
        var displacement = this.getDataset().displacements[index]||0;
        var model = arc._model;
        var angle = model.startAngle + model.circumference/2;
        model.x += Math.cos(angle) * displacement;
        model.y += Math.sin(angle) * displacement;
    }
});

new Chart('chart', {
    type: 'cutOutPie',
    data: {
        labels: ['a', 'b', 'c', 'd', 'e', 'f'],
        datasets: [{
            data: [1, 7, 2, 8, 3, 9],
            backgroundColor: ['red', 'orange', 'green', 'gold', 'pink', 'blue'],
            displacements: [0, 0, 40, 0, 0, 16],
        }],
    },
});
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<canvas id="chart" width="400" height="300"></canvas>

如果我在 React 或 Vue 中打包构建,我知道该怎么做,但不知道如何在浏览器中做同样的事情。

文档似乎只谈论导入和类。

javascript chart.js
1个回答
0
投票

javascript v4,使用 ecmascript 类,因此自定义控制器应该是现有控制器的子类,覆盖相关方法,如文档“新图表”部分中所述

因此,旧代码的类比是:

const PieController = Chart.controllers.pie;

class CutOutPie extends PieController{
    static id = 'cutOutPie';

    updateElement(arc, index, properties, mode) {
        const displacement = this.getDataset().displacements?.[index] || 0;
        if(displacement && properties.circumference){
            const angle = properties.startAngle + properties.circumference/2;
            properties.x += displacement * Math.cos(angle);
            properties.y += displacement * Math.sin(angle);
        }
        if(properties.outerRadius){
            properties.outerRadius -= Math.max(...this.chart.data.datasets[0].displacements);
        }
        super.updateElement(arc, index, properties, mode);
    }
}

Chart.register(CutOutPie);

new Chart('chart', {
    type: 'cutOutPie',
    data: {
        labels: ['a', 'b', 'c', 'd', 'e', 'f'],
        datasets: [{
            data: [1, 7, 2, 8, 3, 9],
            backgroundColor: ['red', 'orange', 'green', 'gold', 'pink', 'blue'],
            displacements: [0, 0, 40, 0, 0, 26],
        }]
    },
    options:{
        responsive: true,
        animation:{
            duration: 500,
            animateRotate: true,
            animateScale: true
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.1.2/chart.umd.js"
        integrity="sha512-t41WshQCxr9T3SWH3DBZoDnAT9gfVLtQS+NKO60fdAwScoB37rXtdxT/oKe986G0BFnP4mtGzXxuYpHrMoMJLA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<body>
<div style="height:500px; width: 500px">
<canvas id="chart" style="border: 1px solid #999"></canvas>
</div>
切片的半径必须按比例缩小,以避免移位的切片延伸到画布之外或与图例或其他元素重叠。

这里有一个稍微复杂的版本,它预先计算位移并考虑标准动画:

const PieController = Chart.registry.controllers.get('pie');
//this seems to be the most general way to get the controller class
// alternatives, depending on environment:
// PieController = Chart.PieController;
// PieController = Chart.controllers.pie;
// import {PieController} from 'chart.js';

class CutOutPie extends PieController{
    static id = 'cutOutPie';
    static defaults = {
        displacements: []
    }

    initialize(){
        super.initialize();
        this._displacementOffset = 0;
        this._offsetsValid = false;
        this._arcProperties = [];
    }

    updateElements(arcs, start, count, mode){
        this._offsetsValid = false;
        this._arcProperties = [];
        super.updateElements(arcs, start, count, mode);

        if(!this._offsetsValid){
            this._computeDisplacementOffsets(arcs);
            for(let i = start; i < start + count; i++){
                this.updateElement(arcs[i], i, {}, mode);
            }
        }
    }

    _computeDisplacementOffsets(arcs){
        if(Number.isFinite(this._arcProperties?.[0]?.outerRadius)){
            let startAngle = this._getRotation();
            for(let i = 0; i < arcs.length; i++){
                const displacement = this.getDataset().displacements?.[i] || 0;
                const endAngle = startAngle + this._circumference(i);
                this._arcProperties[i] = Object.assign(this._arcProperties[i] || {}, {dx: 0, dy: 0, shrink: 1});
                if(displacement){
                    const angle = (startAngle + endAngle)/2;
                    this._arcProperties[i].dx = Math.cos(angle) * displacement;
                    this._arcProperties[i].dy = Math.sin(angle) * displacement;
                    this._displacementOffset = Math.max(this._displacementOffset, displacement);
                }
                startAngle = endAngle;
            }

            for(let i = 0; i < arcs.length; i++){
                const outerRadius = this._arcProperties[i].outerRadius || this.outerRadius;
                if(outerRadius){
                    this._arcProperties[i].shrink =  (outerRadius - this._displacementOffset)/outerRadius ;
                }
            }
            this._offsetsValid = true;
        }
    }

    updateElement(arc, index, properties, mode) {
        this._arcProperties[index] = this._arcProperties[index] || {};
        Object.assign(this._arcProperties[index], properties || {});
        const animation = this.options.animation.animateRotate || this.options.animation.animateScale;
        if(this._offsetsValid){
            if(mode !== 'reset' || !animation){
                if(this._arcProperties[index].hasOwnProperty('dx')){
                    this._arcProperties[index].x += this._arcProperties[index].dx;
                }
                if(this._arcProperties[index].hasOwnProperty('dy')){
                    this._arcProperties[index].y += this._arcProperties[index].dy;
                }
                arc.x = this._arcProperties[index].x - this._arcProperties[index].dx;
                arc.y = this._arcProperties[index].y - this._arcProperties[index].dy;
            }
            const shrink = this._arcProperties[index].shrink;
            if(shrink){
                this._arcProperties[index].outerRadius *= shrink;
                this._arcProperties[index].innerRadius *= shrink;
            }
            super.updateElement(arc, index, this._arcProperties[index], mode);
        }
    }
}

Chart.register(CutOutPie);

new Chart('chart', {
    type: 'cutOutPie',
    data: {
        labels: ['a', 'b', 'c', 'd', 'e', 'f'],
        datasets: [{
            data: [1, 7, 2, 8, 3, 9],
            backgroundColor: ['red', 'orange', 'green', 'gold', 'pink', 'blue'],
            displacements: [0, 0, 40, 0, 0, 26],
        }]
    },
    options:{
        responsive: true,
        animation:{
            duration: 500,
            animateRotate: true,
            animateScale: true
        }
    }
});
<div style="height:500px; width: 500px">
<canvas id="chart" style="border: 1px solid #999"></canvas>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.1.2/chart.umd.js"
        integrity="sha512-t41WshQCxr9T3SWH3DBZoDnAT9gfVLtQS+NKO60fdAwScoB37rXtdxT/oKe986G0BFnP4mtGzXxuYpHrMoMJLA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

© www.soinside.com 2019 - 2024. All rights reserved.