plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.4k stars 2.06k forks source link

[BUG] Animated Graph Conflicts with Animated Figure #1474

Closed ghost closed 3 months ago

ghost commented 3 years ago

Thank you so much for helping improve the quality of Dash!

We do our best to catch bugs during the release process, but we rely on your help to find the ones that slip through.

Describe your context My Dash Server is hosted on WSL2, and browse the App from my browser.

Describe the bug

When both dcc.Graph and go.Figure have animation, then, when the second animation in Figure is executed, the Frames from the first animation are played instead of the second one. There is also a change in the dataset to display.

My findings showed that all the data is correctly sent by the server, and is shown in the callback debug UI. I could not pinpoint exactly where the bug occurs in the JavaScript code as my knowledge is limited. See screenshots for more information.

Expected behavior

The animation from the second (and following) request should be played instead of the animation of the first requested data.

Screenshots

Sorry for the quality of the GIFs!

Expected result:

Expected Result

Observed result:

Observed Result

Reproducible App

Use the following snippet to reproduce easily the issue:

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

fig = go.Figure(
    data=[],
    layout=go.Layout(
        title='Select a dataset to play',
        yaxis=dict(range=[-1, 5]),
        xaxis=dict(range=[-3, 3]),
        updatemenus=[dict(type="buttons",
                          buttons=[dict(label="Play",
                                        method="animate",
                                        args=[None, {"frame": {"duration": 100, "redraw": True},
                                                     "fromcurrent": False,
                                                     "transition": {"duration": 500,
                                                                    "easing": "quadratic-in-out"}}])])]
    ),
    frames=[],
)

app.layout = html.Div([
    html.Label('Choose dataset'),
    dcc.RadioItems(
        id='change-data',
        options=[
            {'label': 'No data', 'value': 0},
            {'label': 'Data A', 'value': 1},
            {'label': 'Data B', 'value': 2}
        ],
        value=0,
    ),
    dcc.Graph(id='test-change', animate=True,  # Set the falue to False to workaround the bug
              animation_options={"frame": {"redraw": True}}),  # Tried with redraw set to True or False with same result
])

@app.callback(
    Output('test-change', 'figure'),
    Input('change-data', 'value'),
)
def set_data(dataset):
    if dataset == 1:
        title = 'Dataset A'
        data = go.Scatter(
            x=[0, 1, 2],
            y=[0, 1, 2],
            mode='markers',
        )
        frames = [
            go.Frame(
                data=go.Scatter(
                    x=[0, 1, 2],
                    y=[1, 2, 3],
                    mode='markers',
                )
            ),
            go.Frame(
                data=go.Scatter(
                    x=[0, 1, 2],
                    y=[2, 3, 4],
                    mode='markers',
                )
            )
        ]
    elif dataset == 2:
        title = 'Dataset B'
        data = go.Scatter(
            x=[0, -1, -2],
            y=[0, 1, 2],
            mode='markers',
        )
        frames = [
            go.Frame(
                data=go.Scatter(
                    x=[0, -1, -2],
                    y=[1, 2, 3],
                    mode='markers',
                )
            ),
            go.Frame(
                data=go.Scatter(
                    x=[0, -1, -2],
                    y=[2, 3, 4],
                    mode='markers',
                )
            )
        ]
    else:
        title = 'Select a dataset'
        data = []
        frames = []

    return go.Figure(
        data=data,
        layout=go.Layout(
            title=title,
            yaxis=dict(range=[-1, 5]),
            xaxis=dict(range=[-3, 3]),
            updatemenus=[dict(type="buttons",
                              buttons=[dict(label="Play",
                                            method="animate",
                                            args=[None, {"frame": {"duration": 100, "redraw": False},  # Tried with redraw set to False with same result
                                                         "fromcurrent": False,
                                                         "transition": {"duration": 500,
                                                                        "easing": "quadratic-in-out"}}])])]
        ),
        frames=frames,
    )

if __name__ == "__main__":
    app.run_server(debug=True)
gvwilson commented 3 months ago

Hi - we are tidying up stale issues and PRs in Plotly's public repositories so that we can focus on things that are most important to our community. If this issue is still a concern, please add a comment letting us know what recent version of our software you've checked it with so that I can reopen it and add it to our backlog. (Please note that we will give priority to reports that include a short reproducible example.) If you'd like to submit a PR, we'd be happy to prioritize a review, and if it's a request for tech support, please post in our community forum. Thank you - @gvwilson