如何将Dash图像悬停追踪的普通回调改为客户端回调以提升性能
Dash悬停回调客户端改造方案
改造要点
- 把高频率触发的悬停数据追加逻辑完全迁移到前端执行,避免每次悬停都发起服务端请求,大幅降低服务端压力、提升响应速度
- 低频率的Next按钮点击逻辑仍保留在服务端,不需要修改原有数据库写入、图片生成的业务逻辑
完整修改后代码
import dash import dash_core_components as dcc import dash_html_components as html import numpy as np import plotly.express as px import requests from dash.dependencies import ClientsideFunction, Input, Output, State from dash.exceptions import PreventUpdate from PIL import Image app = dash.Dash(__name__) server = app.server # In reality, there are 50 screenshot images with non-sequential indexes urls = ["https://pbs.twimg.com/media/EW8GhG_XkAEOyAh.jpg", "https://pbs.twimg.com/media/CqzwpPnWEAAiGjW.jpg"] def url_to_fig(url): rgb_arr = np.array(Image.open(requests.get(url, stream=True).raw)) fig = px.imshow(rgb_arr) fig.update_xaxes(visible=False) fig.update_yaxes(visible=False) fig.update_layout( dragmode=False, width=800, height=800) fig.update_traces(hoverinfo='none', hovertemplate=None) return fig app.layout = html.Div([ dcc.Store(id='ss-idx', data=0), dcc.Graph(id='ss-img', figure=url_to_fig(urls[0]), config = {"displayModeBar": False}), html.Button("Next", id='next-button', n_clicks=0), dcc.Store(id='hoverdata', data=[]), # Place to append new hoverdata ]) # 新增:客户端回调,仅处理悬停数据追加,完全在前端执行 app.clientside_callback( Output('hoverdata', 'data'), Input('ss-img', 'hoverData'), State('hoverdata', 'data'), prevent_initial_call=True, """ function(hover_point, hoverdata) { const ctx = dash_clientside.callback_context; if (!ctx.triggered.length || !hover_point) return dash_clientside.no_update; const x = hover_point.points[0].x; const y = hover_point.points[0].y; hoverdata.push([x, y]); return hoverdata; } """ ) # 修改后的服务端回调,仅处理Next按钮点击逻辑 @app.callback( [Output('ss-idx', 'data'), Output('ss-img', 'figure'), Output('hoverdata', 'data')], Input('next-button', 'n_clicks'), [State('hoverdata', 'data'), State('ss-idx', 'data')], prevent_initial_call=True ) def handle_next_click(next_clicks, hoverdata, ss_idx): # Add hoverdata and screenshot index to mysql database (code not shown) print(next_clicks, hoverdata) if next_clicks < len(urls): new_idx = ss_idx + 1 return new_idx, url_to_fig(urls[new_idx]), [] # Reset hoverdata else: raise PreventUpdate if __name__ == "__main__": app.run_server(debug=True)
关键修改说明
- 新增的客户端回调直接通过内联JS实现逻辑,不需要额外引入JS文件,
prevent_initial_call参数避免页面初始化时无意义触发 - JS逻辑和原Python逻辑完全对齐:判断触发源、提取悬停坐标、追加到存储数组后返回
- 服务端回调去掉了hoverData的触发输入,只有点击Next按钮时才会触发服务端请求,服务端可以直接拿到当前页面完整的悬停数据做数据库写入,原有业务逻辑不需要调整
内容的提问来源于stack exchange,提问作者acciolurker
相关产品推荐
相关产品推荐

