@@ -12,6 +12,7 @@ interface Props {
12
12
}
13
13
14
14
const TOTAL_STAGES = 9
15
+ const BASE_DURATION = 0.5 // Base duration for each stage
15
16
16
17
function EncapsulatedPacket ( props : Props ) {
17
18
const { onComplete, onStageChange } = props
@@ -47,12 +48,15 @@ function EncapsulatedPacket(props: Props) {
47
48
for ( let s = stageRef . current ; s < TOTAL_STAGES ; s ++ ) {
48
49
if ( cancelled ) break
49
50
setStage ( s )
50
- onStageChange ?.( s )
51
51
await controls . start ( {
52
52
left : `${ positions [ s ] . x } %` ,
53
53
top : positions [ s ] . y ,
54
- transition : { duration : 0.5 / props . speed , ease : 'linear' } ,
54
+ transition : {
55
+ duration : BASE_DURATION / props . speed ,
56
+ ease : 'anticipate' ,
57
+ } ,
55
58
} )
59
+ onStageChange ?.( s )
56
60
stageRef . current = s + 1
57
61
if ( ! props . isPlaying ) break
58
62
}
@@ -78,14 +82,6 @@ function EncapsulatedPacket(props: Props) {
78
82
onStageChange ,
79
83
] )
80
84
81
- const visibleHeaders = useMemo ( ( ) => {
82
- const visibleHeaders : number [ ] = [ ]
83
- if ( stage >= 1 && stage <= 7 ) visibleHeaders . push ( 1 )
84
- if ( stage >= 2 && stage <= 6 ) visibleHeaders . push ( 2 )
85
- if ( stage >= 3 && stage <= 5 ) visibleHeaders . push ( 3 )
86
- return visibleHeaders
87
- } , [ stage ] )
88
-
89
85
const Icon = props . packet . type . icon
90
86
const isClientToServer = props . packet . from === 'client'
91
87
const isOnSenderSide = stage < 4
@@ -108,22 +104,29 @@ function EncapsulatedPacket(props: Props) {
108
104
< div className = "w-16 h-12 mt-2 bg-white border-2 border-black rounded flex items-start justify-center" >
109
105
< Icon className = "mt-0.5 w-6 h-6 text-black" />
110
106
</ div >
111
- { visibleHeaders . map ( ( layerIndex , i ) => {
107
+ { [ 1 , 2 , 3 ] . map ( ( layerIndex , i ) => {
112
108
const layer = TCP_IP_LAYERS [ layerIndex ]
113
109
const verticalSpacing = 20
114
110
const verticalOffset = i * verticalSpacing
111
+ let headerVisible = false
112
+ if ( layerIndex === 1 ) headerVisible = stage >= 1 && stage <= 7 // Transport: stages 1-7
113
+ if ( layerIndex === 2 ) headerVisible = stage >= 2 && stage <= 6 // Internet: stages 2-6
114
+ if ( layerIndex === 3 ) headerVisible = stage >= 3 && stage <= 5 // Network Interface: stages 3-5
115
115
return (
116
116
< div key = { `header-${ layerIndex } ` } >
117
- < div
117
+ < motion . div
118
118
className = "absolute inset-0 border-4 rounded"
119
119
style = { {
120
120
borderColor : layer . color ,
121
- transform : `scale(${ 1 + ( visibleHeaders . length - i ) * 0.12 } )` ,
121
+ transform : `scale(${ 1 + ( 3 - i ) * 0.12 } )` ,
122
122
zIndex : - layerIndex ,
123
123
} }
124
+ initial = { { opacity : 0 } }
125
+ animate = { { opacity : headerVisible ? 1 : 0 } }
126
+ transition = { { duration : 0.25 , delay : headerVisible ? 0.5 : 0 } }
124
127
/>
125
128
{ ! isOnTransmission && (
126
- < div
129
+ < motion . div
127
130
className = "absolute text-[8px] font-bold px-1 py-0.5 rounded whitespace-nowrap text-white text-center min-w-18"
128
131
style = { {
129
132
top : `${ verticalOffset } px` ,
@@ -132,9 +135,15 @@ function EncapsulatedPacket(props: Props) {
132
135
transform : `translateX(${ alignRight ? '16px' : '-16px' } )` ,
133
136
backgroundColor : layer . color ,
134
137
} }
138
+ initial = { { opacity : 0 } }
139
+ animate = { { opacity : headerVisible ? 1 : 0 } }
140
+ transition = { {
141
+ duration : 0.25 ,
142
+ delay : headerVisible ? 0.5 : 0 ,
143
+ } }
135
144
>
136
145
{ layer . header }
137
- </ div >
146
+ </ motion . div >
138
147
) }
139
148
</ div >
140
149
)
0 commit comments