Ever wanted your tooltips to smoothly glide across your plots?
Well, now they can
How it Works
The main ingredients:
- Use
dcc.Tooltip
instead of the native tooltip feature - Apply a transition property to the
.dcc-tooltip-bounding-box
- Keep the tooltip always visible for smoothness
A Limitation The tooltip will always stay hovering over the last point you moved over. In other words, the "show"
property is never getting modified, unlike the example this example is based off: see this dopage
This was done to reduce flickering from disappearing and revealing the tooltip when there is no hoverData and when there is hoverData respectively.
The Code
Below, you can find the the code snippets that put this example together, in the files app.py
and styles.css
, which is contained in an assets/
folder in the project.
You can also find the code at GitHub.
app.py
import random
import dash
from dash import dcc, html, Input, Output, State
import plotly
import plotly.graph_objects as go
app = dash.Dash(
__name__,
suppress_callback_exceptions=True,
)
def gen_plotly_figure():
avail_vals = list(range(20))
x_arr = []
y_arr = []
for x in avail_vals:
for y in avail_vals:
plot_pts = random.random() > 0.6
if plot_pts:
x_arr.append(x)
y_arr.append(y)
my_figure = go.Figure(
data=[
go.Scatter(
x=x_arr,
y=y_arr,
mode="markers",
marker=dict(color="#1C1D42"),
hoverinfo="none",
),
],
layout={
"height": 600,
"width": 600,
"plot_bgcolor": "#fafafa",
"paper_bgcolor": "#fafafa",
"xaxis": {
"color": "#f3f3f3",
"tickfont": {
"color": "#666666",
},
"gridcolor": "#f3f3f3",
},
"yaxis": {
"color": "#f3f3f3",
"tickfont": {
"color": "#666666",
},
"gridcolor": "#f3f3f3",
},
},
)
return my_figure
def layout():
return html.Div(
[
dcc.Graph(
id="my-figure", className="my-figure", figure=gen_plotly_figure()
),
dcc.Tooltip(
id="my-tooltip",
className="my-tooltip",
loading_text="",
),
]
)
app.layout = layout
@app.callback(
Output("my-tooltip", "bbox"),
Output("my-tooltip", "children"),
Input("my-figure", "hoverData"),
)
def update_tooltip(hoverData):
if not hoverData:
return dash.no_update, dash.no_update
pt = hoverData["points"][0]
bbox = pt["bbox"]
tooltip_content = html.Div(
[
html.P(f"X: {pt['x']}"),
html.P(f"Y: {pt['y']}"),
]
)
return bbox, tooltip_content
if __name__ == "__main__":
app.run_server(debug=True)
assets/style.css
:root {
--tooltip-color: #FFEEBC;
}
body {
background-color: #fafafa;
font-family: Verdana;
}
.dcc-tooltip-bounding-box {
transition: 0.23s;
animation-timing-function: cubic;
}
.dcc-tooltip-bounding-box .hover::after {
border-color: transparent var(--tooltip-color) transparent transparent !important;
}
.my-tooltip {
color: #222;
font-size: 0.9rem;
background-color: var(--tooltip-color) !important;
border: 0px solid red !important;
height: 4rem !important;
width: 3rem !important;
box-shadow: 0 0 20px rgba(0,0,0,0.08) !important;
}
.my-tooltip * {
padding: 0 !important;
margin: 0 !important;
}