You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用HTMX更新Plotly图表数据并保留当前视图状态(缩放/平移等)

实现方案(FastAPI + HTMX + Plotly.js 增量更新图表)

一、核心思路

放弃每次发送完整图表HTML的方式,改为以下模式:

  • 客户端仅初始化一次Plotly.js图表框架
  • 后端通过WebSocket仅推送更新后的数据(从Plotly Python导出JSON格式)
  • 客户端用Plotly.js的增量更新API修改图表,保留当前视图状态(缩放、平移等)

此方案完全可行,Plotly.js原生支持增量数据更新,结合FastAPI WebSocket和HTMX可实现需求。

二、具体实现步骤

1. 客户端:初始化图表并监听WebSocket更新

在HTML页面中引入Plotly.js,创建图表容器,初始化WebSocket并处理数据更新:

<!-- 引入Plotly.js -->
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
<!-- 图表容器 -->
<div id="chart-container"></div>

<script>
// 初始化WebSocket连接
const ws = new WebSocket(`ws://${window.location.host}/ws/chart-update`);

// 加载初始数据并渲染图表
fetch('/api/initial-chart-data')
  .then(res => res.json())
  .then(initialData => {
    Plotly.newPlot('chart-container', initialData.data, initialData.layout);
  });

// 监听WebSocket消息,增量更新图表
ws.onmessage = function(event) {
  const updateData = JSON.parse(event.data);
  // 仅更新数据,保留视图状态
  Plotly.restyle('chart-container', updateData.data);
  // 若需更新布局,改用Plotly.update():
  // Plotly.update('chart-container', updateData.data, updateData.layout);
};
</script>

2. 后端FastAPI:提供初始数据与WebSocket推送

(1)初始数据接口

用Plotly Python生成初始图表的 data 和 layout,序列化为JSON返回:

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import plotly.graph_objects as go
from plotly.utils import PlotlyJSONEncoder
import json
import asyncio

app = FastAPI()

# 返回初始图表数据
@app.get("/api/initial-chart-data")
async def get_initial_chart_data():
    fig = go.Figure(data=[go.Scatter(x=[1,2,3], y=[4,5,6])])
    fig.update_layout(title="实时更新图表")
    # 序列化Plotly对象为JSON格式
    return {
        "data": json.loads(json.dumps(fig.data, cls=PlotlyJSONEncoder)),
        "layout": json.loads(json.dumps(fig.layout, cls=PlotlyJSONEncoder))
    }

(2)WebSocket连接与数据推送

管理客户端连接,在数据变更时推送增量数据:

# WebSocket连接管理器
class ConnectionManager:
    def __init__(self):
        self.active_connections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def send_update(self, data: dict):
        json_data = json.dumps(data, cls=PlotlyJSONEncoder)
        for connection in self.active_connections:
            await connection.send_text(json_data)

manager = ConnectionManager()

# WebSocket端点
@app.websocket("/ws/chart-update")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            # 模拟数据变更(实际可替换为数据库监听、定时任务等触发逻辑)
            await asyncio.sleep(5)
            # 生成更新后的数据
            new_data = [go.Scatter(x=[1,2,3], y=[4+1,5+1,6+1])]
            # 构造推送 payload(仅含数据部分)
            update_payload = {
                "data": json.loads(json.dumps(new_data, cls=PlotlyJSONEncoder))
            }
            await manager.send_update(update_payload)
    except WebSocketDisconnect:
        manager.disconnect(websocket)

3. 关键细节

  • 视图状态保留:Plotly.restyle()仅更新图表数据,不会重置缩放、平移等用户操作状态;若需调整布局,Plotly.update()同样支持保留视图状态。
  • 数据序列化:PlotlyJSONEncoder可自动将Python端的Plotly对象转换为Plotly.js兼容的JSON格式,无需手动处理数据结构。
  • HTMX WebSocket扩展:若偏好HTMX管理WebSocket,可替换原生WebSocket代码为HTMX的hx-ws属性,回调逻辑保持一致:
    <div hx-ws="connect:/ws/chart-update"></div>
    

内容的提问来源于stack exchange,提问作者Nicola Gigante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:21:13