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
相关产品推荐
相关产品推荐

