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

在Plotly(Python)折线图中实现悬停高亮(非Jupyter环境)

Plotly折线图悬停高亮整条轨迹(兼容PyCharm环境)

因为无法使用FigureWidget,我们可以通过注入自定义JavaScript回调来实现悬停时高亮对应轨迹的效果,以下是兼容px.line和go.Scatter的解决方案:

基于Plotly Express的实现

import plotly.express as px

# 加载并筛选数据
df = px.data.gapminder()
df = df[df['country'].isin(['Canada', 'USA', 'Norway', 'Sweden', 'Germany'])]

# 创建折线图
fig = px.line(df, x='year', y='lifeExp', color='country')

# 设置所有轨迹的初始样式:灰色、宽度3
fig.update_traces(line=dict(color='grey', width=3))

# 添加自定义JS实现悬停高亮
fig.update_layout(
    hovermode='x unified',
    id='hover-highlight-plot',  # 给图表设置唯一ID,方便JS定位
    # 注入隐藏的JS脚本处理悬停事件
    template=dict(
        layout=dict(
            annotations=[
                dict(
                    text=f"""
                    <script>
                    document.addEventListener('DOMContentLoaded', function() {{
                        const plotDiv = document.getElementById('hover-highlight-plot');
                        const traceCount = {len(fig.data)};
                        
                        // 悬停事件处理
                        plotDiv.on('plotly_hover', function(data) {{
                            const hoveredIndex = data.points[0].curveNumber;
                            // 重置所有轨迹样式
                            Plotly.restyle(plotDiv, 'line.width', [3], Array.from({{length: traceCount}}, (_, i) => i));
                            Plotly.restyle(plotDiv, 'line.color', ['grey'], Array.from({{length: traceCount}}, (_, i) => i));
                            // 高亮当前悬停的轨迹
                            Plotly.restyle(plotDiv, 'line.width', [6], [hoveredIndex]);
                            Plotly.restyle(plotDiv, 'line.color', [data.points[0].fullData.line.color], [hoveredIndex]);
                        }});
                        
                        // 离开悬停时恢复样式
                        plotDiv.on('plotly_unhover', function() {{
                            Plotly.restyle(plotDiv, 'line.width', [3], Array.from({{length: traceCount}}, (_, i) => i));
                            Plotly.restyle(plotDiv, 'line.color', ['grey'], Array.from({{length: traceCount}}, (_, i) => i));
                        }});
                    }});
                    </script>
                    """,
                    showarrow=False,
                    xref="paper",
                    yref="paper",
                    x=0,
                    y=0,
                    xanchor="left",
                    yanchor="bottom",
                    font=dict(size=0)  # 隐藏脚本文本
                )
            ]
        )
    )
)

fig.show()

基于Plotly Graph Objects的实现

如果偏好使用go.Scatter,代码逻辑一致:

import plotly.graph_objects as go
import plotly.express as px

df = px.data.gapminder()
df = df[df['country'].isin(['Canada', 'USA', 'Norway', 'Sweden', 'Germany'])]

fig = go.Figure()
# 逐个添加轨迹并设置初始样式
for country in df['country'].unique():
    country_data = df[df['country'] == country]
    fig.add_trace(go.Scatter(
        x=country_data['year'],
        y=country_data['lifeExp'],
        name=country,
        line=dict(color='grey', width=3)
    ))

# 复用相同的JS高亮逻辑
fig.update_layout(
    hovermode='x unified',
    id='hover-highlight-plot',
    template=dict(
        layout=dict(
            annotations=[
                dict(
                    text=f"""
                    <script>
                    document.addEventListener('DOMContentLoaded', function() {{
                        const plotDiv = document.getElementById('hover-highlight-plot');
                        const traceCount = {len(fig.data)};
                        
                        plotDiv.on('plotly_hover', function(data) {{
                            const hoveredIndex = data.points[0].curveNumber;
                            Plotly.restyle(plotDiv, 'line.width', [3], Array.from({{length: traceCount}}, (_, i) => i));
                            Plotly.restyle(plotDiv, 'line.color', ['grey'], Array.from({{length: traceCount}}, (_, i) => i));
                            Plotly.restyle(plotDiv, 'line.width', [6], [hoveredIndex]);
                            Plotly.restyle(plotDiv, 'line.color', [data.points[0].fullData.line.color], [hoveredIndex]);
                        }});
                        
                        plotDiv.on('plotly_unhover', function() {{
                            Plotly.restyle(plotDiv, 'line.width', [3], Array.from({{length: traceCount}}, (_, i) => i));
                            Plotly.restyle(plotDiv, 'line.color', ['grey'], Array.from({{length: traceCount}}, (_, i) => i));
                        }});
                    }});
                    </script>
                    """,
                    showarrow=False,
                    xref="paper",
                    yref="paper",
                    x=0,
                    y=0,
                    xanchor="left",
                    yanchor="bottom",
                    font=dict(size=0)
                )
            ]
        )
    )
)

fig.show()

效果说明

  • 初始状态:所有折线为灰色,宽度3
  • 悬停时:对应国家的折线会恢复原本的颜色,宽度增加到6,其余折线保持灰色窄线
  • 离开悬停区域后,所有折线恢复初始样式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:30:58