[英]How do I synchronize the zoom level of multiple charts with Plotly.js?
我使用 Plotly.js 創建了 3 個時間序列圖,並且我希望保持縮放級別/平移同步,因此如果用戶縮放/平移 3 個圖中的任何一個,則其他 2 個得到更新。
現在我在第一個圖表上為plotly_relayout
事件設置了一個事件處理程序。 事件處理程序使用傳入的事件數據調用其他兩個圖表上的Plotly.relayout
。
有誰知道如何確定哪個圖首先發出了plotly_relayout
事件,這樣它就不會陷入處理自己事件的循環中? 或者也許有更好的方法來處理保持多個圖表之間的縮放/平移同步?
正如 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>
我在此處復制@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>
根據@RMS(@ChrisG 的)和@Alex 的答案工作,當我想同時縮放 x 和 y 時仍然遇到問題(瀏覽器在放大方向之一或縮小時會完全凍結)。 我剛剛開始使用 js 來實現這個功能,但我通過這種方式使它適用於任何類型的縮放:
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: { 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: { 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: { 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; let y = div.layout.yaxis; var update = {}; if ("xaxis.autorange" in ed && ed["xaxis.autorange"] != x.autorange) { update['xaxis.autorange']= ed["xaxis.autorange"]; } if ("yaxis.autorange" in ed && ed["yaxis.autorange"] != y.autorange) { update['yaxis.autorange'] = ed["yaxis.autorange"]; } if ("xaxis.range[0]" in ed && ed["xaxis.range[0]"] != x.range[0]) { update['xaxis.range[0]'] = ed["xaxis.range[0]"]; } if ("xaxis.range[1]" in ed && ed["xaxis.range[1]"] != x.range[1]) { update['xaxis.range[1]'] = ed["xaxis.range[1]"]; } if ("yaxis.range[0]" in ed && ed["yaxis.range[0]"] != y.range[0]) { update['yaxis.range[0]'] = ed["yaxis.range[0]"]; } if ("yaxis.range[1]" in ed && ed["yaxis.range[1]"] != y.range[1]) { update['yaxis.range[1]'] = ed["yaxis.range[1]"]; } Plotly.relayout(div, update); }); } divs.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>
這里還有一個 codepen 鏈接。
類似的事情可以在 plotly dash 中完成。 代碼片段如下。
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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.