如何在 React 中使 Chart.js 图表具有固定纵横比的响应能力?

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

我正在开发一个 React 项目,我需要在网格布局的父组件内显示 Chart.js 图表。图表应根据其父容器调整大小,同时保持固定的长宽比 3.5:1(宽度:高度)。但是,我正在努力使图表具有响应性,而不对其尺寸进行硬编码。

这是我当前的设置:

父组件

   <div className="graph_container">
      <h4>{title}</h4>
      <div className="graph_container_graph">
        <DynamicLineChart data={data} key={JSON.stringify(data)} />
      </div>
    </div>

父组件的样式

.graph_container {
  width: 100%;
  max-width: fit-content;
  border-radius: 1.27rem;
  background-color: #fff;
  padding: 0.95rem;
  display: flex;
  flex-direction: column;
  gap: 0.63rem;
  font-size: 1.4rem;
}

.graph_container_graph {
  padding: 0 0.95rem;
}

动态折线图组件

import React, { useEffect } from 'react';
import { Line } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';

ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);

const DynamicLineChart = ({ data }) => {
  const chartData = {
    labels: Object.keys(data),
    datasets: [
      {
        data: Object.values(data),
        fill: false,
        backgroundColor: '#FFF',
        borderColor: '#6497FC',
        borderWidth: 3,
        pointBackgroundColor: '#4D5C92',
        pointBorderColor: '#4D5C92',
        pointRadius: 5,
        pointHoverRadius: 5,
      },
    ],
  };

  const options = {
    aspectRatio: 3.5,
    maintainAspectRatio: true,
    layout: {
      padding: {},
    },
    plugins: {
      legend: {
        display: false,
      },
      tooltip: {
        enabled: true,
      },
    },
    scales: {
      x: {
        grid: {
          borderDash: [15, 5],
          color: '#CDD1DB',
          borderColor: '#CDD1DB',
        },
        ticks: {
          color: '#6B7280',
          font: {
            size: 10,
          },
        },
      },
      y: {
        min: 0,
        max: 100,
        ticks: {
          font: {
            size: 10,
          },
          stepSize: 25,
          color: '#6B7280',
        },
        grid: {
          display: false,
        },
      },
    },
  };

  return (
    <div style={{ width: '100%', height: 'auto' }}>
      <Line data={chartData} options={options} />
    </div>
  );
};

export default DynamicLineChart;

**问题**是,目前,图表没有响应,除非我对其宽度和高度进行硬编码。我希望图表根据其父容器的宽度调整大小,保持纵横比为 3.5:1。我怎样才能实现这个目标?

我尝试过的

  1. 在图表选项中将
    maintainAspectRatio 
    设置为 true。
  2. Line 
    组件包装在具有基于百分比的宽度的 div 中。
  3. 使用CSS控制父容器的大小。 这些解决方案都没有达到预期效果。图表无法正确调整大小,并且经常破坏布局。

我需要图表根据父容器动态调整其宽度,同时保持高度与宽度成比例,纵横比为3.5:1

任何帮助或建议将不胜感激!

css reactjs charts chart.js responsive
1个回答
0
投票

通过查看您的图表选项,我想说图表应该具有响应能力, 因为选项

responsive: true
已打开(默认情况下如此); 这意味着图表将根据容器 div 调整其大小。

没有可运行的示例 无法确定,但我认为很可能正在发生的事情 是容器 div 没有按照您的预期调整大小。

考虑一下:

  • 第一步(可选),减小容器 div 容器的宽度;图表 (这在
    aspectRatio: 3.5
    处完美地填充了容器 - 因为容器 有
    height: auto
    ) 将收缩,容器的高度 (
    auto
    ) 将随之收缩,并且 减少,图表总是完美地填充容器。
  • 然后,增加容器容器的宽度 - 容器的宽度 容器(
    100%
    )将跟随并增加相同的值。然而现在, 容器的高度没有理由增加,它将保持不变,并且 图表,受容器高度限制根本不会改变,创建 没有反应的样子

以下代码片段使用您发布的部分代码,说明了这种动态:

const data = {
    labels: Array.from({length: 12}, (_, i)=>'L'+(i+1)),
    datasets: [{
        data: Array.from({length: 12}, ()=>5+90*Math.random()),
        fill: false,
        backgroundColor: '#FFF',
        borderColor: '#6497FC',
        borderWidth: 3,
        pointBackgroundColor: '#4D5C92',
        pointBorderColor: '#4D5C92',
        pointRadius: 5,
        pointHoverRadius: 5,
    }]
};

const options = {
    aspectRatio: 3.5,
    maintainAspectRatio: true,
    layout: {
        padding: {},
    },
    plugins: {
        legend: {
            display: false,
        },
        tooltip: {
            enabled: true,
        },
    },
    scales: {
        x: {
            grid: {
                borderDash: [15, 5],
                color: '#CDD1DB',
                borderColor: '#CDD1DB',
            },
            ticks: {
                color: '#6B7280',
                font: {
                    size: 10,
                },
            },
        },
        y: {
            min: 0,
            max: 100,
            ticks: {
                font: {
                    size: 10,
                },
                stepSize: 25,
                color: '#6B7280',
            },
            grid: {
                display: false,
            },
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
const containerEl = document.querySelector('#container'),
    container2El = document.querySelector('#containerOfContainer'),
    chartEl = document.querySelector('#myChart'),
    textarea = document.querySelector('textarea'),
    shrinkBut = document.querySelector('#shrink'),
    enlargeBut = document.querySelector('#enlarge');


let count = 1;
function updateTextArea(){
    const idx = (count < 10 ? ' ' : '') + count++,
        wCont = containerEl.offsetWidth,
        hCont = containerEl.offsetHeight,
        wChart = chartEl.offsetWidth,
        hChart = chartEl.offsetHeight;
    textarea.value = `${idx}.  h = ${hCont} w = ${wCont} (div)  |   h = ${hChart} w = ${wChart} (chart)\n` + textarea.value;
}

let wInt = 3;
shrinkBut.onclick = function(){
    disableButtons();
    container2El.style.width = (container2El.offsetWidth - 30)+'px';
    wInt--;
    setTimeout(()=>{enableButtons(); updateTextArea();}, 1000);
}

enlargeBut.onclick = function(){
    disableButtons();
    container2El.style.width = (container2El.offsetWidth + 30)+'px';
    wInt++;
    setTimeout(()=>{enableButtons(); updateTextArea();}, 1000);
}

function disableButtons(){
    shrinkBut.setAttribute('disabled', 'd');
    enlargeBut.setAttribute('disabled', 'd');
}
function enableButtons(){
    if(wInt > 0){
        shrinkBut.removeAttribute('disabled');
    }
    if(wInt < 3){
        enlargeBut.removeAttribute('disabled');
    }
}

updateTextArea();
<div id="containerOfContainer" style="width: 90vw; border: 1px solid black">
    <div id="container" style="width: 100%; height: auto; border:1px solid red">
        <canvas style="border:1px solid blue" id="myChart"></canvas>
    </div>
</div>
<button id="shrink">Shrink</button> <button id="enlarge" disabled>Enlarge</button>
<br/> Container sizes (<strong>most recent on top</strong>) <br/>
<textarea style="width: 70ex; height: 10ex"></textarea>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

在这种情况下,解决方案是使用容器 div 的 css - 将

height
属性替换为
aspect-ratio
。 我最初认为
aspectRatio
Chart.js 属性指的是图表区域; 然而,在多次尝试计算理想的容器纵横比之后, 我意识到实际上明确说明了什么 文档 它指的是画布。这大大简化了事情,因为什么并不重要 图表画布包含,右侧是否有图例或顶部是否有标题等。理想的 容器的css和图表的
aspectRatio
一样,解决办法是

  <div style={{ width: '100%', aspectRatio: 3.5 }}>
     <Line data={chartData} options={options} />
  </div>

这是经过修改的上述片段:

const data = {
    labels: Array.from({length: 12}, (_, i)=>'L'+(i+1)),
    datasets: [{
        data: Array.from({length: 12}, ()=>5+90*Math.random()),
        fill: false,
        backgroundColor: '#FFF',
        borderColor: '#6497FC',
        borderWidth: 3,
        pointBackgroundColor: '#4D5C92',
        pointBorderColor: '#4D5C92',
        pointRadius: 5,
        pointHoverRadius: 5,
    }]
};

const options = {
    aspectRatio: 3.5,
    maintainAspectRatio: true,
    layout: {
        padding: {},
    },
    plugins: {
        legend: {
            display: false,
        },
        tooltip: {
            enabled: true,
        },
    },
    scales: {
        x: {
            grid: {
                borderDash: [15, 5],
                color: '#CDD1DB',
                borderColor: '#CDD1DB',
            },
            ticks: {
                color: '#6B7280',
                font: {
                    size: 10,
                },
            },
        },
        y: {
            min: 0,
            max: 100,
            ticks: {
                font: {
                    size: 10,
                },
                stepSize: 25,
                color: '#6B7280',
            },
            grid: {
                display: false,
            },
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
const containerEl = document.querySelector('#container'),
    container2El = document.querySelector('#containerOfContainer'),
    chartEl = document.querySelector('#myChart'),
    textarea = document.querySelector('textarea'),
    shrinkBut = document.querySelector('#shrink'),
    enlargeBut = document.querySelector('#enlarge');


let count = 1;
function updateTextArea(){
    const idx = (count < 10 ? ' ' : '') + count++,
        wCont = containerEl.offsetWidth,
        hCont = containerEl.offsetHeight,
        wChart = chartEl.offsetWidth,
        hChart = chartEl.offsetHeight;
    textarea.value = `${idx}.  h = ${hCont} w = ${wCont} (div)  |   h = ${hChart} w = ${wChart} (chart)\n` + textarea.value;
}

let wInt = 3;
shrinkBut.onclick = function(){
    disableButtons();
    container2El.style.width = (container2El.offsetWidth - 30)+'px';
    wInt--;
    setTimeout(()=>{enableButtons(); updateTextArea();}, 1000);
}

enlargeBut.onclick = function(){
    disableButtons();
    container2El.style.width = (container2El.offsetWidth + 30)+'px';
    wInt++;
    setTimeout(()=>{enableButtons(); updateTextArea();}, 1000);
}

function disableButtons(){
    shrinkBut.setAttribute('disabled', 'd');
    enlargeBut.setAttribute('disabled', 'd');
}
function enableButtons(){
    if(wInt > 0){
        shrinkBut.removeAttribute('disabled');
    }
    if(wInt < 3){
        enlargeBut.removeAttribute('disabled');
    }
}

updateTextArea();
<div id="containerOfContainer" style="width: 90vw; border: 1px solid black">
    <div id="container" style="width: 100%; aspect-ratio: 3.5; border:1px solid red">
        <canvas style="border:1px solid blue" id="myChart"></canvas>
    </div>
</div>
<button id="shrink">Shrink</button> <button id="enlarge" disabled>Enlarge</button>
<br/> Container sizes (<strong>most recent on top</strong>) <br/>
<textarea style="width: 70ex; height: 10ex"></textarea>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

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