Here’s an approach using a clientside callback based on an answer in a different thread from @RenaudLN. Because the callback is triggered once the layout has been rendered, you can use it to defer execution of JS until Dash has finished loading.
Note that when I first implemented this the use of html.Canvas
was giving me some problems. Changing it to html.Div
helped.
# app.py
import dash
import dash_bootstrap_components as dbc
from dash import ClientsideFunction, Input, Output, html
app = dash.Dash(
__name__,
external_stylesheets=[
"https://fonts.googleapis.com/css?family=Poppins",
dbc.themes.MINTY,
],
external_scripts=[
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js",
"https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.cells.min.js",
],
)
app.layout = html.Div(
children=[
html.Div(
dbc.Container(
html.H1(
"Demo Dash app with Vanta.JS", style={"color": "#ff3d3d"}
),
className="py-5",
),
id="vanta-container",
style={"height": "100vh", "width": "100vw"},
),
],
)
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="attach_vanta"),
Output("vanta-container", "id"),
[Input("vanta-container", "id")],
)
if __name__ == "__main__":
app.run_server(debug=True)
and in assets/
// assets/custom.js
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
attach_vanta: function (id) {
setTimeout(function () {
var el = document.getElementById(id);
VANTA.CELLS({ el });
}, 1);
return window.dash_clientside.no_update;
},
};