在 React 应用程序中使 D3 图表具有响应性

Posted

技术标签:

【中文标题】在 React 应用程序中使 D3 图表具有响应性【英文标题】:Making D3 chart Responsive in React app 【发布时间】:2016-07-25 12:22:45 【问题描述】:

我目前正在尝试使用 D3 在 React 中渲染绘图。为了整合它们,我使用了以下链接:

http://oli.me.uk/2015/09/09/d3-within-react-the-right-way/。

我还尝试了链接中提供的使图表响应的解决方案:

Resize svg when window is resized in d3.js

虽然它在与 React 集成后在独立代码中运行良好,但它开始省略 ticks 。单独为 D3 工作 jsfiddle:https://jsfiddle.net/adityap16/11edxrnq/1/

独立 D3 代码:

var data = [
"mytime": "2015-12-01T11:10:00.000Z", "value": 64,
"mytime": "2015-12-01T11:15:00.000Z", "value": 67,
"mytime": "2015-12-01T11:20:00.000Z", "value": 70,
"mytime": "2015-12-01T11:25:00.000Z", "value": 64,
"mytime": "2015-12-01T11:30:00.000Z", "value": 72,
"mytime": "2015-12-01T11:35:00.000Z", "value": 75,
"mytime": "2015-12-01T11:40:00.000Z", "value": 71,
"mytime": "2015-12-01T11:45:00.000Z", "value": 80
];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;

data.forEach(function(d) 
          d.mytime = parseDate(d.mytime);
        );
//var margin =  top: 30, right: 30, bottom: 40, left:50 ,
var margin =  top: 30, right: 30, bottom: 40, left:50 ,
height = 200,
width = 800;
var color =  "green";
var xaxis_param = "mytime";
var yaxis_param = "value"
var params1 =  margin:margin,height:height,width:width, color: color, xaxis_param:xaxis_param, yaxis_param :yaxis_param;
draw_graph(data,params1);




function  draw_graph(data,params)


    //Get the margin 
    var xaxis_param = params.xaxis_param;
    var yaxis_param = params.yaxis_param;
    var color_code = params.color;
    var margin = params.margin;
    var height = params.height - margin.top - margin.bottom,
        width = params.width - margin.left - margin.right;

    var x_extent = d3.extent(data, function(d)
        return d[xaxis_param]);
    var y_extent = d3.extent(data, function(d)
        return d[yaxis_param]);

    var x_scale = d3.time.scale()
        .domain(x_extent)
        .range([0,width]);


    var y_scale = d3.scale.linear()
        .domain([0,y_extent[1]])
        .range([height,0]);



    //Line
    var lineGen = d3.svg.line()
        .x(function (d) 
            return x_scale(d[xaxis_param]);
        )
        .y(function (d) 
            return y_scale(d[yaxis_param]);
        );
    var myChart = d3.select('body')
              .append("div")
             .classed("svg-container", true)
             .append('svg')
     .attr("preserveAspectRatio", "xMinYMin meet")
   .attr("viewBox", "0 0 800 600")
   //class to make it responsive
   .classed("svg-content-responsive", true)    
                    .attr('class','my-chart')
                    .style('background', '#E7E0CB')

                    .append('g')
                    .attr('transform', 'translate('+ margin.left +', '+ margin.top +')');
            myChart
                    .append('svg:path')
                    .datum(data)
                    .attr('class', 'line')
                    .attr("d",lineGen)
                    .attr('stroke', color_code)
                    .attr('stroke-width', 1)
                    .attr('fill', 'none');


    var legend = myChart.append("g")
          .attr("class", "legend")
          .attr("transform", "translate(" + 5 + "," + (height - 25) + ")")

        legend.append("rect")
          .style("fill", color_code)
          .attr("width", 20)
          .attr("height", 20);

        legend.append("text")
          .text(yaxis_param)
          .attr("x", 25)
          .attr("y", 12);

    var vGuideScale = d3.scale.linear()
        .domain([0,y_extent[1]])
        .range([height, 0])

    var vAxis = d3.svg.axis()
        .scale(vGuideScale)
        .orient('left')
        .ticks(5)


    var hAxis = d3.svg.axis()
        .scale(x_scale)
        .orient('bottom')
        .ticks(d3.time.minute, 5);

  myChart.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(hAxis);

  myChart.append("g")
      .attr("class", "y axis")
      .call(vAxis)



我可能需要在调整大小时设置 setState 或其他东西,这应该会触发 React 使用新信息重新渲染,但是对于 React 来说是新手,我无法使其工作。

编辑:我仍在尝试编辑,但问题似乎是 x 轴和 y 轴上的刻度,当您缩小窗口时,您可以看到刻度但不是全屏。仍在试图理解为什么?

谢谢!

【问题讨论】:

除了 React 之外应该有错误导致 svg 没有响应。毕竟,viewboxwidth: 100%; height: 100% 是使 svg 响应的全部内容。尝试查看您的 DOM 结构,以确保 svg 的父级响应开始。 不会导致错误,xaxis 和 yaxis 上的刻度都被省略了。我尝试调整视图框中的宽度和高度以及 .css 文件中的底部填充,但无济于事。 你能在 jsfiddle 上重新创建问题吗?通过查看损坏的副本而不是工作副本更容易发现问题。 反应组件在另一个反应组件中。作为 React 的新手,不确定我是否可以让它在 jsfiddle 中工作。我的道歉 确保 svg 元素本身也有 width="100%"height="100%"。另外,检查是否没有可能具有静态大小的中间 DOM 元素。 【参考方案1】:

所以我能够在 React on Github(react-d3 组件)中的一个未解决问题中找到答案。附上链接和代码sn-p:

https://github.com/codesuki/react-d3-components/issues/9

代码:

let AreaGraph = React.createClass(
    mixins: [React.addons.PureRenderMixin],

    getInitialState() 
        return 
            parentWidth: 0
        
    ,

    getDefaultProps() 
        return 
            width: '100%',
            height: 300,
            margin:  left: -1, top: 10, bottom: 0, right: 1 
        
    ,

    handleResize(e) 
        let elem = this.getDOMNode();
        let width = elem.offsetWidth;

        this.setState(
            parentWidth: width
        );
    ,

    componentDidMount() 
        if(this.props.width === '100%') 
            window.addEventListener('resize', this.handleResize);
        
        this.handleResize();
    ,

    componentWillUnmount() 
        if(this.props.width === '100%') 
            window.removeEventListener('resize', this.handleResize);
        
    ,

    render() 
        let  width, height, margin, xScale, yScale, xAxis, ...props  = this.props;

        // Determine the right graph width to use if it's set to be responsive
        if(width === '100%') 
            width = this.state.parentWidth || 400;
        

        // Set scale ranges
        xScale && xScale.range([0, width - (margin.left + margin.right)]);
        yScale && yScale.range([height - (margin.top + margin.bottom), 0]);

        return (
            <div className="usage__cpu__graph "+props.className>
                <AreaChart
                    ref="chart"
                    width=width
                    height=height
                    margin=margin 
                    xScale=xScale
                    yScale=yScale
                    xAxis=xAxis
                    ...props 
                />
            </div>
        );
    
)

【讨论】:

以上是关于在 React 应用程序中使 D3 图表具有响应性的主要内容,如果未能解决你的问题,请参考以下文章

响应式 D3 js 图表 [重复]

Vega 图表 - 如何使图表具有响应性

在 D3 v4 中使 SVG 容器具有父容器的 100% 宽度和高度(而不是像素)

响应式 D3 图表

D3 Chart SVG响应条和轴

如何使 React Material-UI 中的 GridList 组件具有响应性