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

Sigma.js如何实现悬停节点时节点与边的平滑颜色过渡动画

sigma.js 节点悬停2秒平滑颜色过渡实现方案

sigma.js 基于Canvas渲染,原生不支持类CSS的自动属性过渡,结合官方提供的Reducer状态渲染机制+逐帧颜色插值,就能实现自定义时长的平滑悬停变色效果,全程不需要修改框架源码,所有逻辑基于公开API实现。


核心实现步骤

  • 存储节点悬停状态、原始颜色、过渡时长配置
  • 编写十六进制颜色插值工具,计算过渡过程中的中间色
  • 绑定节点鼠标事件,通过requestAnimationFrame驱动逐帧更新
  • 通过Reducer动态返回当前帧的节点颜色,触发重渲染

1. 初始化状态配置

先定义全局状态存储,统一管理过渡相关参数:

// 悬停状态管理
const hoverState = {
  activeNode: null, // 当前悬停的节点ID
  startTime: 0, // 过渡开始时间戳
  originalColors: new Map(), // 缓存节点原始填充色
  duration: 2000 // 过渡时长,单位毫秒,设置为2000即2秒
}
// 自定义悬停目标色,可按需修改
const HOVER_TARGET_COLOR = '#ff3b30';

2. 实现颜色插值工具

Canvas渲染需要手动计算两个颜色之间的过渡值,先写两个颜色格式转换和插值的工具函数:

// 十六进制色转RGB数组
function hexToRgb(hex) {
  const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return match ? [
    parseInt(match[1], 16),
    parseInt(match[2], 16),
    parseInt(match[3], 16)
  ] : [0, 0, 0];
}
// RGB值转回十六进制色
function rgbToHex(r, g, b) {
  return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// 根据过渡进度计算中间色
function getLerpColor(startHex, endHex, progress) {
  const start = hexToRgb(startHex);
  const end = hexToRgb(endHex);
  const r = Math.round(start[0] + (end[0] - start[0]) * progress);
  const g = Math.round(start[1] + (end[1] - start[1]) * progress);
  const b = Math.round(start[2] + (end[2] - start[2]) * progress);
  return rgbToHex(r, g, b);
}

3. 绑定鼠标事件启动过渡循环

监听sigma实例的节点进出事件,启动逐帧渲染循环更新过渡进度:

// 初始化sigma实例后绑定事件
const renderer = new Sigma(graph, container);

// 鼠标移入节点
renderer.on('enterNode', ({ node }) => {
  // 缓存节点原始颜色
  if (!hoverState.originalColors.has(node)) {
    hoverState.originalColors.set(node, graph.getNodeAttribute(node, 'color'));
  }
  hoverState.activeNode = node;
  hoverState.startTime = Date.now();
  runTransition();
});

// 鼠标移出节点
renderer.on('leaveNode', () => {
  hoverState.activeNode = null;
});

// 逐帧过渡循环
function runTransition() {
  const elapsed = Date.now() - hoverState.startTime;
  let progress = Math.min(elapsed / hoverState.duration, 1);
  // 可选:添加缓动效果,这里用easeOutCubic让过渡更自然,不需要可以删掉
  progress = 1 - Math.pow(1 - progress, 3);
  // 将当前进度写入节点属性,触发reducer计算颜色
  if (hoverState.activeNode) {
    graph.setNodeAttribute(hoverState.activeNode, 'hoverProgress', progress);
  }
  renderer.refresh();
  // 过渡未完成则继续下一帧
  if (progress < 1 || hoverState.activeNode) {
    requestAnimationFrame(runTransition);
  }
}

4. 配置节点Reducer动态返回颜色

和官方Reducer的标准用法一致,渲染节点时根据当前进度计算对应颜色即可:

renderer.setSetting('nodeReducer', (nodeId, nodeData) => {
  const renderData = { ...nodeData };
  const progress = nodeData.hoverProgress || 0;
  const originalColor = hoverState.originalColors.get(nodeId) || nodeData.color;
  if (progress > 0) {
    renderData.color = getLerpColor(originalColor, HOVER_TARGET_COLOR, progress);
  }
  return renderData;
});

扩展说明

  • 如果需要鼠标移出节点时也平滑过渡回原始色,只需要在leaveNode事件中记录离开起始时间,在插值逻辑中增加反向进度计算即可,核心逻辑和进入过渡完全一致。
  • 修改hoverState.duration的值可以自由调整过渡时长,不需要改动其他代码。
  • 替换runTransition函数中的缓动公式,可以实现线性、弹性等不同的过渡手感。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:06:23