如何为 google material 条形图制作动画

How to animate a google material bar chart

我正在 React 项目中绘制一个 React google 条形图(material 条形图),我正在尝试制作动画。我读过这种图表不支持动画,但我需要这样做,必须有任何方法可以做到。我很难认为新事物比旧事物更糟糕。任何人都知道我该怎么做?我尝试了很多不同的方法,但没有任何效果。这是我的代码:

import React from 'react';
import './App.css';
import Chart from 'react-google-charts'

function App() {

  return (
    <div className="App">      
      <Chart
        width={'500px'}
        height={'300px'}
        // Note here we use Bar instead of BarChart to load the material design version
        chartType="Bar"
        loader={<div>Loading Chart</div>}
        data={[
          ['City', '2010 Population', '2000 Population'],
          ['New York City, NY', 8175000, 8008000],
          ['Los Angeles, CA', 3792000, 3694000],
          ['Chicago, IL', 2695000, 2896000],
          ['Houston, TX', 2099000, 1953000],
          ['Philadelphia, PA', 1526000, 1517000],
        ]}
        options={{
          // Material chart options
          chart: {
            title: 'Population of Largest U.S. Cities',
            subtitle: 'Based on most recent and previous census data',
          },
          hAxis: {
            title: 'Total Population',
            minValue: 0,
          },
          animation: {
            duration: 1000,
            easing: 'out',
            startup: true,
          },
          vAxis: {
            title: 'City',
          },
          bars: 'horizontal',
          axes: {
            y: {
              0: { side: 'right' },
            },
          },
        }}
      />
    </div>
  );
}

export default App;

您可以尝试在短时间后通过交换图表数据来模拟动画。这是我的建议,分为 3 个步骤。

  1. 最初加载图表时图表值为“0”。
  2. 然后加载数据的部分值。
  3. 最后设置真实的数据值。
function ChartBox() {

  let initialData = [
    ['City', '2010 Population', '2000 Population'],
    ['New York City, NY', 0, 0],
    ['Los Angeles, CA', 0, 0],
    ['Chicago, IL', 0, 0],
    ['Houston, TX', 0, 0],
    ['Philadelphia, PA', 0, 0],
  ];

  let n = 250; // divider
  let dataLoading = [
    ['City', '2010 Population', '2000 Population'],
    ['New York City, NY', 8175000/n, 8008000/n],
    ['Los Angeles, CA', 3792000/n, 3694000/n],
    ['Chicago, IL', 2695000/n, 2896000/n],
    ['Houston, TX', 2099000/n, 1953000/n],
    ['Philadelphia, PA', 1526000/n, 1517000/n],
  ];

  let finalData = [
    ['City', '2010 Population', '2000 Population'],
    ['New York City, NY', 8175000, 8008000],
    ['Los Angeles, CA', 3792000, 3694000],
    ['Chicago, IL', 2695000, 2896000],
    ['Houston, TX', 2099000, 1953000],
    ['Philadelphia, PA', 1526000, 1517000],
  ];

  const [chartData, setChartData] = useState(initialData);

  useEffect(() => {
    const timer = setTimeout(() => {
      setChartData(dataLoading)
    }, 100);
    const timer2 = setTimeout(() => {
      setChartData(finalData)
    }, 300);
    return () => {clearTimeout(timer); clearTimeout(timer2)}
  }, []);

  return (
    <div className="App">
      <Chart
        {...}
        data={chartData}
        {...}

使用 State Hook 和 useEffect 有助于操作我们想要呈现的数据。在 <Chart/> 组件中,我传递了 chartData,该值将在 100 毫秒和 300 毫秒后发生变化。当然,您可以添加更多带有部分值的步骤(如数据加载),这样您的“动画”看起来会更流畅。

Demo using react | Demo Using vanilla javascript

google material 图表不支持动画。

如果要向 material google 图表添加动画,可以使用 css 动画手动完成。让我们开始吧 (Demo):

首先我们应该得到一个实际柱状图的选择器。似乎第三个 svg 组(g 标签)是图表中的实际条形图(其他组用于标签/标题/等):

.animated-chart g:nth-of-type(3) {...}

然后我们应该添加一个 css 过渡到它:

.animated-chart g:nth-of-type(3) {
  transition: 1s;
}

然后我们可以创建一个 class (.animated-chart-start) 用于在 transform: scaleX(1);transform: scaleX(0); 之间切换,如下所示:

.animated-chart g:nth-of-type(3) {
  transition: 1s;
  transform: scaleX(1);
}
.animated-chart.animated-chart-start g:nth-of-type(3) {
  transform: scaleX(0);
}

到目前为止我们添加了 css,现在我们应该将这些 class 添加到我们的图表中,并在短暂的延迟后切换 .animated-chart-start class。我们可以在 componentDidMount 上完成,但在图表准备就绪时更干净:

<Chart
    ...
    className={`animated-chart animated-chart-start`}
    chartEvents={[
      {
        eventName: "ready",
        callback: ({ chartWrapper, google }) => {
          const chartEl = chartWrapper.getChart().container;
          setTimeout(() => {
            chartEl.classList.remove('animated-chart-start')
          }, 100)
        },
      }
    ]}
  />

它将 .animated-chart-start class 添加到图表中,并在 100 毫秒后将其删除。 (100 毫秒是可选的,您也可以立即切换它)。

另请注意,google 图表似乎不支持将数据绑定到 className(如 className={this.state.dynamicClass}),这就是为什么我们不能将状态变量用于切换动画 class.

最后,我们可以像 AnimatedChart 一样将这个动画图表包装到一个单独的组件中,以使其更易于重用。 (你可以在 stackblitz 代码上看到它)。

Run it live

已知限制:

  • 在图表动画期间设置状态会导致 re-render 并破坏 css 转换。
  • 我们假设第三个 svg 组是图表。但它可能会因图表类型甚至图表属性而异。

更新:对于垂直图表,您可以使用 scaleY 来制作动画,并且您可能需要设置变换原点,如:transform-origin: 0 calc(100% - 50px); 以使其看起来更好。 (Run vertical version On Stackblitz)

更新 2:对于 vanilla javascript 版本(没有任何框架),请参阅 here

刚刚更新了代码并尝试 re-implement 以更好的方式解决它,但找不到更好的解决方案。

你需要和CSS一起玩

对于Y-axis动画

g:nth-of-type(3) transition: 2s; transform: scaleX(1);

对于X-axis动画

g:nth-of-type(3) transform: scaleX(0);

https://codesandbox.io/s/google-react-chart-do602?file=/src/styles.css