如何使用Plotly.js同步多个图表的缩放级别?

时间:2017-12-21 22:24:43

标签: javascript charts plotly

我使用Plotly.js创建了3个时间序列图,我想保持缩放级别/平移同步,所以如果用户放大/平移3个图中的任何一个,则另外2个会更新。

现在我在plotly_relayout事件的第一个图表上设置了一个事件处理程序。事件处理程序使用传入的事件数据在另外两个图表上调用Plotly.relayout

有没有人知道如何识别首先发出plotly_relayout事件的情节,以便它不会陷入处理自己事件的循环中?或者可能有更好的方法来处理多个图表之间的缩放/平移同步?

请参阅some grpc tests上的Restless Minds Studio(Linked Zoom Events in Plotly.js Charts)的笔@restlessmindsstudio

3 个答案:

答案 0 :(得分:0)

我在这里复制@ChrisG的评论给予他信任,并在回答时显示我的问题:

  

找到了一个更好的解决方案:https://codepen.io/anon/pen/NXRpzW?editors=1010

var myDiv = document.getElementById("myDiv");
var myDiv2 = document.getElementById("myDiv2");
var myDiv3 = document.getElementById("myDiv3");
var startTime = new Date().getTime();
var timeStep = 60000;
var myDivMax = 70;
var myDiv2Max = 9000;
var myDiv3Max = 500;

var d3 = Plotly.d3,
  N = 40,
  x = d3.range(N).map(() => {
    return new Date((startTime += timeStep));
  }),
  y = d3.range(N).map(() => Math.random() * myDivMax),
  data = [{ x: x, y: y }];
var layout = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};
var layout2 = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};
var layout3 = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};

Plotly.plot(myDiv, data, layout);
var data2 = [{ x: x, y: d3.range(N).map(() => Math.random() * myDiv2Max) }];
Plotly.plot(myDiv2, data2, layout2);
var data3 = [{ x: x, y: d3.range(N).map(() => Math.random() * myDiv3Max) }];
Plotly.plot(myDiv3, data3, layout3);

var divs = [myDiv, myDiv2, myDiv3];

function relayout(ed, divs) {
  divs.forEach((div, i) => {
    let x = div.layout.xaxis;
    if (ed["xaxis.autorange"] && x.autorange) return;
    if (
      x.range[0] != ed["xaxis.range[0]"] ||
      x.range[1] != ed["xaxis.range[1]"]
    )
      Plotly.relayout(div, ed);
  });
}

var plots = [myDiv, myDiv2, myDiv3];
plots.forEach(div => {
  div.on("plotly_relayout", function(ed) {
    console.log(ed);
    relayout(ed, divs);
  });
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<h2>Resize any chart, the others will get the same zoom level applied</h2>
<div id="myDiv"></div>
<div id="myDiv2"></div>
<div id="myDiv3"></div>

答案 1 :(得分:0)

正如Ashish Rathi所注意到的,上述解决方案存在一个错误-当您尝试按某些按钮时,代码执行会进入无限循环。解决方案是在自己的重传处理程序中检查“ ed”变量,如果没有属性,则退出。在某些情况下,Plotly会发送带有包含更新目的的空对象的plotly_relayout事件只是出于娱乐目的-可能是某种错误。

在我的情况下,我不想让Y缩放从一个图传播到另一个图-因此,我准备了一个单独的结构,仅包含那些我想传播的属性。

var myDiv = document.getElementById("myDiv");
var myDiv2 = document.getElementById("myDiv2");
var myDiv3 = document.getElementById("myDiv3");
var startTime = new Date().getTime();
var timeStep = 60000;
var myDivMax = 70;
var myDiv2Max = 9000;
var myDiv3Max = 500;

var d3 = Plotly.d3,
  N = 40,
  x = d3.range(N).map(() => {
    return new Date((startTime += timeStep));
  }),
  y = d3.range(N).map(() => Math.random() * myDivMax),
  data = [{ x: x, y: y }];
var layout = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};
var layout2 = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};
var layout3 = {
  height: 200,
  margin: { l: 45, t: 5, r: 45, b: 45 },
  xaxis: {
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  },
  yaxis: {
    fixedrange: true,
    tickfont: {
      size: 10,
      color: "#7f7f7f"
    }
  }
};

Plotly.plot(myDiv, data, layout);
var data2 = [{ x: x, y: d3.range(N).map(() => Math.random() * myDiv2Max) }];
Plotly.plot(myDiv2, data2, layout2);
var data3 = [{ x: x, y: d3.range(N).map(() => Math.random() * myDiv3Max) }];
Plotly.plot(myDiv3, data3, layout3);

var divs = [myDiv, myDiv2, myDiv3];

function relayout(ed, divs) {
  if (Object.entries(ed).length === 0) {return;}
  divs.forEach((div, i) => {
    let x = div.layout.xaxis;
    if (ed["xaxis.autorange"] && x.autorange) return;
    if (x.range[0] != ed["xaxis.range[0]"] ||x.range[1] != ed["xaxis.range[1]"])
    {
      var update = {
      'xaxis.range[0]': ed["xaxis.range[0]"],
      'xaxis.range[1]': ed["xaxis.range[1]"],
      'xaxis.autorange': ed["xaxis.autorange"],
     };
     Plotly.relayout(div, update);
    }
  });
}

var plots = [myDiv, myDiv2, myDiv3];
plots.forEach(div => {
  div.on("plotly_relayout", function(ed) {
    relayout(ed, divs);
  });
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<h2>Resize any chart, the others will get the same zoom level applied</h2>
<div id="myDiv"></div>
<div id="myDiv2"></div>
<div id="myDiv3"></div>

答案 2 :(得分:-1)

类似的事情可以用破折号来完成。代码段如下。

app=dash.Dash()

app.layout = html.Div([
            dcc.Graph(id='graph',figure=fig),
            html.Pre(id='relayout-data', style=styles['pre']),
            dcc.Graph(id='graph2', figure=fig)])

@app.callback(Output('relayout-data', 'children'),
              [Input('graph', 'relayoutData')])
def display_relayout_data(relayoutData):
    return json.dumps(relayoutData, indent=2)


@app.callback(Output('graph2', 'figure'),
             [Input('graph', 'relayoutData')], 
             [State('graph2', 'figure')])
def graph_event(select_data,  fig):
    try:
       fig['layout'] = {'xaxis':{'range':[select_data['xaxis.range[0]'],select_data['xaxis.range[1]']]}}
    except KeyError:
       fig['layout'] = {'xaxis':{'range':[zoomed out value]}}
return fig

app.run_server()