Update position of vertical line

I am trying to update the position of a vertical line based on the currentTime of a video. The figure is defined with python in dash. However, I want to update the line position with js without redrawing the entire figure.

In python the figure is defined like this:

    fig = go.Figure(data=go.Scatter(x=timeline, y=data, name='Data'))

    fig.add_shape(
        # Line Vertical
        dict(
            type="line",
            x0=0,
            y0=0,
            x1=0,
            y1=1,
            line=dict(
                color="black",
                width=3,
            )
        )
    )

and displayed in dash with this code:

dcc.Graph(
    id='graph',
    figure=fig,
)

In js I am using Plotly.update because Plotly.relayout does not seem to be available for me. The js code looks like this:

window.dash_clientside = Object.assign({}, window.dash_clientside, {
    clientside: {
        verticalLine: function(currentTime) {

            // Get the DOM element of the Plotly graph
            var graphDiv = document.getElementById('graph');

            Plotly.update(
                graphDiv,
                {},
                {
                    'shapes[0]': {
                        x0: currentTime,
                        x1: currentTime
                    }
                }
            )
        }
    }
});

The verticalLine function is called by a clientside callback in dash:

    app.clientside_callback(
        ClientsideFunction(
            namespace='clientside',
            function_name='verticalLine'
        ),

        Output('graph', 'figure'),
        Input('video', 'currentTime'),
        prevent_initial_call=True
    )

The video element is a dash player that exposes the currentTime property. When running this code the following error shows up in the console:

TypeError: undefined is not an object (evaluating 't.data.map')

Any ideas on how this could be fixed are appreciated.

Solved this by passing in the figure as State and using dash-extensions to omit the Output that is usually required by dash.

from dash_extensions.enrich import DashProxy, NoOutputTransform, Input, State


app = DashProxy(__name__, transforms=[NoOutputTransform()])

app.clientside_callback(
    ClientsideFunction(
        namespace='clientside',
        function_name='drawVerticalLine'
    ),

    Input('video', 'currentTime'),
    State('graph', 'figure'),
    prevent_initial_call=True
)

The js now looks like this:

window.dash_clientside = Object.assign({}, window.dash_clientside, {
    clientside: {

        // Update the vertical line position
        drawVerticalLine: function(currentTime, fig1) {

            function drawLine (fig, time, id) {
                fig.layout.shapes[0].x0 = time;
                fig.layout.shapes[0].x1 = time;
                Plotly.react(id , fig.data, fig.layout, {displayModeBar: false});
            }

            drawLine(fig1, currentTime, 'graph');

        }
    }
});

To make the line movement smoother you can update the intervalCurrentTime property of the dash-player to e.g. 10ms. Hope this helps anyone who might have a similar issue :slight_smile:

1 Like