1+ import * as d3 from 'd3' ;
2+
3+ /**
4+ * 添加节点被选中时的波纹动画效果 - 蓝色波纹动画
5+ * 类似于第一张图中的效果,蓝色节点被选中时产生多层波纹
6+ */
7+ export const addNodeSelectedEffect = (
8+ svg : d3 . Selection < SVGSVGElement , unknown , null , undefined > ,
9+ x : number ,
10+ y : number ,
11+ nodeRadius : number ,
12+ color : string = '#3498db' , // 默认蓝色
13+ scale : number = 1
14+ ) => {
15+ const group = svg . append ( 'g' )
16+ . attr ( 'class' , 'node-selected-effect' )
17+ . attr ( 'transform' , `translate(${ x } , ${ y } )` ) ;
18+
19+ // 添加多层波纹效果
20+ for ( let i = 0 ; i < 3 ; i ++ ) {
21+ const delayFactor = i * 700 ; // 错开动画开始时间
22+
23+ // 添加波纹圆圈
24+ const circle = group . append ( 'circle' )
25+ . attr ( 'r' , nodeRadius )
26+ . attr ( 'fill' , 'none' )
27+ . attr ( 'stroke' , color )
28+ . attr ( 'stroke-width' , 2.5 * scale )
29+ . attr ( 'stroke-opacity' , 0.8 )
30+ . attr ( 'stroke-dasharray' , '3,3' ) // 虚线效果
31+ . style ( 'transform-origin' , 'center' )
32+ . style ( 'transform-box' , 'fill-box' ) ;
33+
34+ // 第一个动画:扩散
35+ const animateRipple = ( ) => {
36+ circle
37+ . attr ( 'stroke-opacity' , 0.8 )
38+ . attr ( 'r' , nodeRadius )
39+ . transition ( )
40+ . duration ( 2000 )
41+ . attr ( 'r' , nodeRadius * 3 )
42+ . attr ( 'stroke-opacity' , 0 )
43+ . on ( 'end' , animateRipple ) ; // 循环动画
44+ } ;
45+
46+ // 延迟启动动画
47+ setTimeout ( animateRipple , delayFactor ) ;
48+ }
49+
50+ // 添加内部发光效果
51+ const innerGlow = group . append ( 'circle' )
52+ . attr ( 'r' , nodeRadius * 1.1 )
53+ . attr ( 'fill' , 'none' )
54+ . attr ( 'stroke' , color )
55+ . attr ( 'stroke-width' , 3 * scale )
56+ . attr ( 'filter' , 'url(#glow-filter)' ) ;
57+
58+ // 脉冲动画
59+ const pulseInnerGlow = ( ) => {
60+ innerGlow
61+ . transition ( )
62+ . duration ( 1000 )
63+ . attr ( 'stroke-opacity' , 1 )
64+ . attr ( 'r' , nodeRadius * 1.2 )
65+ . transition ( )
66+ . duration ( 1000 )
67+ . attr ( 'stroke-opacity' , 0.5 )
68+ . attr ( 'r' , nodeRadius * 1.1 )
69+ . on ( 'end' , pulseInnerGlow ) ; // 循环动画
70+ } ;
71+
72+ pulseInnerGlow ( ) ;
73+
74+ return group ;
75+ } ;
76+
77+ /**
78+ * 添加指针节点相交时的特效 - 黄色/橙色的相交高亮效果
79+ * 类似于第二张图中的效果,当两个指针相交时产生的特殊黄色高亮
80+ */
81+ export const addPointersIntersectionEffect = (
82+ svg : d3 . Selection < SVGSVGElement , unknown , null , undefined > ,
83+ x : number ,
84+ y : number ,
85+ nodeRadius : number ,
86+ scale : number = 1
87+ ) => {
88+ const group = svg . append ( 'g' )
89+ . attr ( 'class' , 'pointers-intersection-effect' )
90+ . attr ( 'transform' , `translate(${ x } , ${ y } )` ) ;
91+
92+ // 添加外部光环 - 黄色渐变
93+ const outerHalo = group . append ( 'circle' )
94+ . attr ( 'r' , nodeRadius * 1.6 )
95+ . attr ( 'fill' , 'none' )
96+ . attr ( 'stroke' , '#f39c12' )
97+ . attr ( 'stroke-width' , 3 * scale )
98+ . attr ( 'stroke-opacity' , 0.3 )
99+ . attr ( 'filter' , 'url(#glow-filter)' ) ;
100+
101+ // 添加第二层光环
102+ const middleHalo = group . append ( 'circle' )
103+ . attr ( 'r' , nodeRadius * 1.3 )
104+ . attr ( 'fill' , 'none' )
105+ . attr ( 'stroke' , '#f1c40f' )
106+ . attr ( 'stroke-width' , 3 * scale )
107+ . attr ( 'stroke-opacity' , 0.5 ) ;
108+
109+ // 添加内部光环
110+ const innerHalo = group . append ( 'circle' )
111+ . attr ( 'r' , nodeRadius * 1.1 )
112+ . attr ( 'fill' , 'none' )
113+ . attr ( 'stroke' , '#f39c12' )
114+ . attr ( 'stroke-width' , 4 * scale )
115+ . attr ( 'stroke-opacity' , 0.7 ) ;
116+
117+ // 脉冲动画 - 外部光环
118+ const pulseOuterHalo = ( ) => {
119+ outerHalo
120+ . transition ( )
121+ . duration ( 1500 )
122+ . attr ( 'stroke-opacity' , 0.5 )
123+ . attr ( 'r' , nodeRadius * 1.8 )
124+ . transition ( )
125+ . duration ( 1500 )
126+ . attr ( 'stroke-opacity' , 0.3 )
127+ . attr ( 'r' , nodeRadius * 1.6 )
128+ . on ( 'end' , pulseOuterHalo ) ;
129+ } ;
130+
131+ // 脉冲动画 - 中间光环
132+ const pulseMiddleHalo = ( ) => {
133+ middleHalo
134+ . transition ( )
135+ . duration ( 1200 )
136+ . attr ( 'stroke-opacity' , 0.7 )
137+ . attr ( 'r' , nodeRadius * 1.4 )
138+ . transition ( )
139+ . duration ( 1200 )
140+ . attr ( 'stroke-opacity' , 0.5 )
141+ . attr ( 'r' , nodeRadius * 1.3 )
142+ . on ( 'end' , pulseMiddleHalo ) ;
143+ } ;
144+
145+ // 脉冲动画 - 内部光环
146+ const pulseInnerHalo = ( ) => {
147+ innerHalo
148+ . transition ( )
149+ . duration ( 900 )
150+ . attr ( 'stroke-opacity' , 0.9 )
151+ . attr ( 'r' , nodeRadius * 1.15 )
152+ . transition ( )
153+ . duration ( 900 )
154+ . attr ( 'stroke-opacity' , 0.7 )
155+ . attr ( 'r' , nodeRadius * 1.1 )
156+ . on ( 'end' , pulseInnerHalo ) ;
157+ } ;
158+
159+ // 启动所有动画
160+ pulseOuterHalo ( ) ;
161+ setTimeout ( ( ) => pulseMiddleHalo ( ) , 300 ) ; // 错开动画开始时间
162+ setTimeout ( ( ) => pulseInnerHalo ( ) , 600 ) ; // 错开动画开始时间
163+
164+ return group ;
165+ } ;
0 commit comments