JavaScript canvas 饼图变形问题

JavaScript canvas pie chart warping issue

我正在尝试创建自定义 HTML 元素,以便轻松创建饼图。 到目前为止,我一直非常成功,除了我似乎无法让实际的 canvas 绘图按比例正确。

class PieChart extends HTMLElement {
    constructor(){
        super();
    }

    connectedCallback(){
        this.classList.add('pie-chart')
        this.innerHTML = `<canvas style="width:100%; height:100%;"></canvas>`
        this.style.display = 'block'
        this.ctx = this.getElementsByTagName('canvas')[0].getContext('2d')

        const results = [
            {mood: "Angry", total: 1599, shade: "#0a9627"},
            {mood: "Happy", total: 478, shade: "#960A2C"},
            {mood: "Melancholic", total:332, shade: "#332E2E"},
            {mood: "Gloomy", total: 195, shade: "#F73809"},
            {mood: "Eh", total: 195, shade: "#000000"}
        ];

            this.chartify(results);
        

    }

    chartify(results){
        var el_width = this.getBoundingClientRect().width;
        var half = (el_width / 2);
        let sum = 0;
        let portion = results.reduce((sum, {total}) => sum + total, 0);
        let currentAngle = 0;
    
        for (let moodValue of results) {
            //calculating the angle the slice (portion) will take in the chart
            let portionAngle = (moodValue.total / portion) * 2 * Math.PI;
            //drawing an arc and a line to the center to differentiate the slice from the rest
            this.ctx.beginPath();
            this.ctx.arc(half, half, half, currentAngle, currentAngle + portionAngle);
            currentAngle += portionAngle;
            this.ctx.lineTo(half, half);
            //filling the slices with the corresponding mood's color
            this.ctx.fillStyle = moodValue.shade;
            this.ctx.fill();
        }
    }
}

window.customElements.define('pie-chart', PieChart);
export{PieChart};

这是返回结果

自定义元素设计为具有动态大小。因此,为了绘制弧线,我将宽度除以 2 以获得中点。我想这也是半径。那么我所需要的就是计算我没有问题的角度。为什么饼图是长方形的,没有占满space?我的 canvas 绘制代码有什么问题?

canvas elementheightwidth 属性的默认值分别为 150300。如果您需要 canvas 是正方形,则需要将这些尺寸指定为相同。

此示例调整您的 el_width 变量以直接读取 canvas 的宽度,而不是使用 getBoundingClientRect。我还调整了 canvas 元素的创建方式,使其具有 widthheight 属性,其 100% 宽度和高度样式通过 CSS 设置:

class PieChart extends HTMLElement {
    constructor(){
        super();
    }

    connectedCallback(){
        this.classList.add('pie-chart')
        this.innerHTML = `<canvas width="300" height="300"></canvas>`
        this.style.display = 'block'
        this.ctx = this.getElementsByTagName('canvas')[0].getContext('2d')

        const results = [
            {mood: "Angry", total: 1599, shade: "#0a9627"},
            {mood: "Happy", total: 478, shade: "#960A2C"},
            {mood: "Melancholic", total:332, shade: "#332E2E"},
            {mood: "Gloomy", total: 195, shade: "#F73809"},
            {mood: "Eh", total: 195, shade: "#000000"}
        ];

            this.chartify(results);
        

    }

    chartify(results){
        var el_width = this.ctx.canvas.width;
        var half = (el_width / 2);
        let sum = 0;
        let portion = results.reduce((sum, {total}) => sum + total, 0);
        let currentAngle = 0;
    
        for (let moodValue of results) {
            //calculating the angle the slice (portion) will take in the chart
            let portionAngle = (moodValue.total / portion) * 2 * Math.PI;
            //drawing an arc and a line to the center to differentiate the slice from the rest
            this.ctx.beginPath();
            this.ctx.arc(half, half, half, currentAngle, currentAngle + portionAngle);
            currentAngle += portionAngle;
            this.ctx.lineTo(half, half);
            //filling the slices with the corresponding mood's color
            this.ctx.fillStyle = moodValue.shade;
            this.ctx.fill();
        }
    }
}

window.customElements.define('pie-chart', PieChart);
pie-chart {
  width: 150px;
  aspect-ratio: 1 / 1;
}

pie-chart canvas {
  width: 100%;
  height: 100%;
}
<pie-chart></pie-chart>

您还会注意到,如果您使用的 widthheight 值小于显示 canvas 的实际大小,它会明显放大因为它生成的是光栅图像而不是矢量图像。