【问题标题】:Resize highcharts using react-grid-layout not working使用 react-grid-layout 调整 highcharts 的大小不起作用
【发布时间】:2017-03-18 17:52:04
【问题描述】:

我正在研究 react 并使用带有 react-grid-layout 的 highcharts 来调整 div 中元素的大小。不知何故,可调整大小的功能适用于图像,但不适用于高图。 Grid.js 是一个制作 ResponsiveReactGridLayout 的文件,它从 chart.js 文件中获取 highchart。请在下面找到代码。

//Grid.js
import React, { Component } from 'react';
import {Responsive, WidthProvider,ReactGridLayout} from 'react-grid-layout';
import Charts from './charts.js';

const ResponsiveReactGridLayout = WidthProvider(Responsive);

class Grid extends Component {

    onLayoutChange(layout) {
       console.log(layout);
    }

    render(){
        return (<div style={{borderStyle: 'groove'}}>
                <h2> Panel Header </h2>
                <ResponsiveReactGridLayout className="layout"
                                           breakpoints={{lg: 1200, md: 96, sm: 768}}
                                           cols={{lg: 5, md: 10, sm: 6}}
                                           onLayoutChange={this.onLayoutChange}>

                    <div key="c" data-grid={{x: 0, y: 0, w: 1, h: 3}} style={{ border:'1px solid green', borderStyle: 'groove'}}>
                        <img src="https://cdn.geckoandfly.com/wp-content/uploads/2013/03/530-smiley-face.jpg" style={{width:'inherit', height:'inherit'}} />
                    </div>
                    <div key="d" className = 'react-grid-item react-resizable'
                         data-grid={{x: 1, y: 0, w: 1, h: 3}} style={{ border:'1px solid green', borderStyle: 'groove'}}
                        >
                        <Charts style={{width:'inherit'}} id={'Chart 1'}/>
                    </div>
                    <div key="e" data-grid={{x: 2, y: 0, w: 1, h: 3}} style={{ border:'1px solid green',borderStyle: 'groove'}}>
                        <Charts style={{width:'inherit'}} id={'Chart 2'}/>
                    </div>
                    <div key="f" data-grid={{x: 3, y: 0, w: 1, h: 3}} style={{ border:'1px solid green',borderStyle: 'groove'}}>
                        <Charts style={{width:'inherit'}} id={'Chart 3'}/>
                    </div>
                </ResponsiveReactGridLayout>
            </div>
        )
    }
}
export default (Grid);


//Charts.js
import React, { Component } from 'react';
const Highcharts = require('highcharts');
class Charts extends Component{
    constructor(props){
        super(props);
        this.state = {
                data : {
                xAxis: {
                    categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
                },
                series: [{
                    data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 295.6, 454.4]
                }]
            },
            pieData: [{name: "Firefox",y: 6},{name: "MSIE",y: 4},{name: "Safari",y: 4},{name: "Opera",y: 1},{name: "Chrome",y: 7}]
        };
    }

    /**
     * Inject highcharts markup into the DOM after the rest of the component has mounted
     * @return {None}
     */
    componentDidMount() {
        // Load in any highcharts modules
        if (this.props.modules) {
            this.props.module.forEach((module) => {
                module(Highcharts);
            });
        }

        // Create the actual chart and assign reference
        const props = this.processPropsModel(this.props);
        const containerRef = `container${props.id}`;
        this.chart = new Highcharts.chart(
            containerRef,
            props.options
        );
    }

    processPropsModel(props) {
        const newProps = {};
        newProps.id = this.props.id;
        newProps.options = this.generateDefaultOptions();
        return newProps;
    }

    /**
     * Generating some default chart options for placeholding purposes
     * @return {Object}       The options to be passed into the chart
     */
    generateDefaultOptions() {
        return {

            title: {
                text: this.props.id,
                x: -20 //center
            },
            xAxis: {
                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
            },
            yAxis: {
                title: {
                    text: null
                }
            },
            tooltip: {
                valueSuffix: '°C'
            },
            series: [{
                name: 'Tokyo',
                data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
            }]
        };
    }

    render(){
        const props = this.processPropsModel(this.props);
        return (
            <div id={`container${props.id}`}>
            </div>
        );
    }
}
export default Charts;

【问题讨论】:

  • 这个问题可能更多地与chartjs有关,而不是react-grid-layout。我遇到了同样的困难,并尝试了react-dimensions,所以我可以动态地将容器宽度和高度应用于 chartjs 包装器。它反应灵敏,但会产生奇怪、模糊/扭曲的结果。我会继续戳它,chartjs 很可爱。

标签: reactjs highcharts


【解决方案1】:

所以,我在使用 highcharts-react-official 和 react-grid-layout 时遇到了同样的问题。

这就是我最终得到它的方法。


tl;博士

  1. 将高度 100% 分配给图表的所有父项,直至网格项。
  2. highcharts 自己创建了一个烦人的 div。找到一种方法来识别它并赋予它 100% 的高度。
  3. 为图表本身指定高度 100%。
  4. 使用 react highcharts 回调来获取图表对象。
  5. 当您的组件更新时重排您的图表。

下面是我的响应式网格布局,只是为了提供一些上下文。

// Component/Grid/Grid.js
<ResponsiveGridLayout
    ...
>
    {this.state.widgets.map((widget) =>
        <Card key={widget.DWG_ID}>
            <Widget
                widget={widget}
            />
        </Card>
    )}
</ResponsiveGridLayout>

现在,在 Widget 组件中,将作为 highchart 父级的任何 div 的高度设置为 100%。

// Component/Widget/Widget.js
<CardBody className="widgetContent">
    <CardTitle className="widget-title">{this.props.widget.DWG_LABEL}</CardTitle>
    <Chart      
        widget={this.props.widget}
    />}     
</CardBody>   

对于上面的 jsx,我只需要对带有类 widgetContent 的 CardBody 元素执行此操作,所以

// Component/Widget/Widget.css
.widgetContent { height: 100%; }

现在,在图表组件中(最有趣的地方),我必须创建一个非常丑陋的 div,以便能够识别 highcharts 创建的最外层 div。

elements created by highcharts

问题中臭名昭著的 div 可以在上图中看到,就在类 highchartsWrapper 的 div 下方,属性为 data-highcharts-chart 。 这个 div 是我无法直接识别以给出 100% 高度的图表的唯一父级。所以我创建了包装器,以便能够明确地识别它。 请注意,在图表选项中,我们还传递了一个类名,以便能够为图表本身赋予 CSS 高度属性。

如果有人知道如何识别这个有问题的 div,请告诉我。

// Component/Chart/Chart.js
options = {
    ...
    chart: { className: 'chart' }
}
<div className="highchartsWrapper">
    <HighchartsReact
        highcharts={Highcharts}
        options={options}
        callback={(chart) => this.setChart(chart)}
    />
</div>

所以我可以给它css

// Component/Chart/Chart.css
.highchartsWrapper > div {
    height: 100%;
}
.chart {
    height: 100%;
}

现在您的 highchart 将理想地假定正确的宽度和高度。但是还有另一个复杂情况:当第一次渲染 highchart 并检查他父母的身高时,react-grid-layout 还没有完成他的调整大小魔法。这意味着您的图表将非常小。此外,当您调整网格项目的大小时,您希望 highchart 调整为新的父大小。但是等等,我以前使用过 highcharts,我知道该怎么做!好旧的 chart.reflow() !不幸的是,这并没有那么容易。

首先,仅获取可以调用回流的图表对象并不是很简单。如果你注意到了,我给了我的 HighchartsReact 一个回调

(chart) => this.setChart(chart)

这只是将图表对象存储为我的类组件的属性。我的 setChart 函数只执行以下操作:

setChart(chart) {
    this.chart = chart;
}

这样做可能看起来很愚蠢。为什么不直接将 setChart 赋予 HighchartsReact 的回调属性呢?好吧,因为如果你这样做,根据 highcharts-react-official 文档,你在 setChart 函数中的 this 将是你的图表对象......这一切都非常令人困惑,但它似乎像这样工作。

再次提醒,如果有人有更好的解决方案,请告诉我

最后,我们可以在图表组件更新时调用 this.chart.reflow()。我所做的是类似于

constructor() {
    super(props)

    this.firstResize = true;
}

componentDidUpdate(prevProps) {

    if (this.didWidgetSizeChange(prevProps) || this.isFirstResize) {
        this.chart.reflow();
        this.isFirstResize = false;
    }
}

当组件更新时,我们调用图表重排。在 componentDidMount 上,网格项还没有最终大小,所以我使用了一个标志来确定第一次更新(就是这样:网格项已经完成了第一次调整大小)。然后对于任何其他更新,我编写了一个函数,该函数基本上将此网格项的先前布局与新布局进行比较,以确定大小或宽度是否已更改。如果是这样,我们再次重排以将 highcharts 调整为新的网格项大小。

希望这会有所帮助!

和平!

【讨论】:

    【解决方案2】:

    这是使其适合网格的示例解决方案....

    import React from 'react';
    import './App.css';
    import '/node_modules/react-grid-layout/css/styles.css';
    import '/node_modules/react-resizable/css/styles.css';
    import GridLayout from 'react-grid-layout';
    import Highcharts from "highcharts/highstock";
    import HighchartsReact from "highcharts-react-official";
    
    const options = {
      series: [
        {
          data: [1, 2, 3]
        }
      ]
    };
    
    class MyFirstGrid extends React.Component {
      constructor(props) {
        super(props);
        this.myRef = React.createRef();
        this.conRef = React.createRef();
      }
    
      render() {
        // layout is an array of objects, see the demo for more complete usage
        var layout = [
          { i: "a", x: 0, y: 0, w: 5, h: 5 },
          { i: "b", x: 1, y: 0, w: 3, h: 2 },
          { i: "c", x: 4, y: 0, w: 1, h: 2 }
        ];
        return (
          <GridLayout
            className="layout"
            layout={layout}
            cols={12}
            rowHeight={30}
            width={1200}
            onResizeStop={function(event) {
    
             this.myRef.current.chart.setSize(this.conRef.current.clientWidth,this.conRef.current.clientHeight)
              console.log('hello', event);
            }.bind(this)}
          >
            <div ref={this.conRef}  style={{ backgroundColor: "#00000000" }} key="a">
              <HighchartsReact
                ref= {this.myRef}
                containerProps={{ style: { width: '100%', height: '100%' } }}
                options={options}
                highcharts={Highcharts}
              />
            </div>
    
            <div style={{ backgroundColor: "red" }} key="b">
              b
            </div>
            <div style={{ backgroundColor: "blue" }} key="c">
              c
            </div>
          </GridLayout>
        );
      }
    }
    
    
    export default MyFirstGrid;
    

    【讨论】:

      【解决方案3】:

      我认为调整highchrts大小的功能不起作用的原因可以从the documentation of 'reflow' from hightcharts找到它

      默认情况下,在 window.resize 事件之后,图表会根据 chart.reflow 选项自动重排到其容器中。但是,对于 div 调整大小没有可靠的事件,因此如果在没有窗口调整大小事件的情况下调整容器大小,则必须显式调用。

      情况与您现在所做的相符:您尝试调整 div 本身的大小而不是窗口大小,因此它没有按您的预期工作。

      我做了什么使我的图表在网格中可调整大小如下:

      1. 创建一个函数来进行回流
      const onLayoutChange = () => {
          for (let i = 0; i < Highcharts.charts.length; i += 1) {
            if (Highcharts.charts[i] !== undefined) {
              Highcharts.charts[i].reflow(); // here is the magic to update charts' looking
            }
          }
        };
      
      1. 使用react-grid-layout提供的onLayoutChange
      <GridLayout
          cols={12}
          layout={layout}
          rowHeight={30}
          width={1200}
          onLayoutChange={() => onLayoutChange()}
      >
      ...
      </GridLayout>
      

      然后……砰!您可以通过react-grid-layout 中的调整大小按钮控制可调整大小的图表。

      让它易于理解,您可以在此处查看我的游乐场: https://codesandbox.io/s/resize-highcharts-in-grid-9e625?file=/src/App.js

      【讨论】:

        猜你喜欢
        • 2021-03-11
        • 1970-01-01
        • 2019-01-21
        • 1970-01-01
        • 2023-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-22
        相关资源
        最近更新 更多