4
4
using ReactNative . UIManager . Events ;
5
5
using System ;
6
6
using System . Collections . Generic ;
7
+ using System . Linq ;
7
8
using static System . FormattableString ;
8
9
9
10
namespace ReactNative . Animated
@@ -31,9 +32,14 @@ class NativeAnimatedNodesManager : IEventDispatcherListener
31
32
private readonly IDictionary < int , AnimatedNode > _animatedNodes = new Dictionary < int , AnimatedNode > ( ) ;
32
33
private readonly IList < AnimationDriver > _activeAnimations = new List < AnimationDriver > ( ) ;
33
34
private readonly IList < AnimatedNode > _updatedNodes = new List < AnimatedNode > ( ) ;
34
- private readonly IDictionary < Tuple < int , string > , EventAnimationDriver > _eventDrivers = new Dictionary < Tuple < int , string > , EventAnimationDriver > ( ) ;
35
+ // Mapping of a view tag and an event name to a list of event animation drivers. 99% of the time
36
+ // there will be only one driver per mapping so all code should be optimized around that.
37
+ private readonly IDictionary < Tuple < int , string > , IList < EventAnimationDriver > > _eventDrivers =
38
+ new Dictionary < Tuple < int , string > , IList < EventAnimationDriver > > ( ) ;
35
39
private readonly IReadOnlyDictionary < string , object > _customEventTypes ;
36
40
private readonly UIImplementation _uiImplementation ;
41
+ // Used to avoid allocating a new array on every frame in `RunUpdates` and `OnEventDispatch`
42
+ private readonly IList < AnimatedNode > _runUpdateNodeList = new List < AnimatedNode > ( ) ;
37
43
38
44
private int _animatedGraphBFSColor = 0 ;
39
45
@@ -283,12 +289,42 @@ public void AddAnimatedEventToView(int viewTag, string eventName, JObject eventM
283
289
284
290
var pathList = eventMapping [ "nativeEventPath" ] . ToObject < string [ ] > ( ) ;
285
291
var @event = new EventAnimationDriver ( pathList , valueNode ) ;
286
- _eventDrivers . Add ( Tuple . Create ( viewTag , eventName ) , @event ) ;
292
+ var key = Tuple . Create ( viewTag , eventName ) ;
293
+ if ( _eventDrivers . ContainsKey ( key ) )
294
+ {
295
+ _eventDrivers [ key ] . Add ( @event ) ;
296
+ }
297
+ else
298
+ {
299
+ var drivers = new List < EventAnimationDriver > ( 1 ) ;
300
+ drivers . Add ( @event ) ;
301
+ _eventDrivers . Add ( key , drivers ) ;
302
+ }
287
303
}
288
304
289
- public void RemoveAnimatedEventFromView ( int viewTag , string eventName )
305
+ public void RemoveAnimatedEventFromView ( int viewTag , string eventName , int animatedValueTag )
290
306
{
291
- _eventDrivers . Remove ( Tuple . Create ( viewTag , eventName ) ) ;
307
+ var key = Tuple . Create ( viewTag , eventName ) ;
308
+ if ( _eventDrivers . ContainsKey ( key ) )
309
+ {
310
+ var driversForKey = _eventDrivers [ key ] ;
311
+ if ( driversForKey . Count == 1 )
312
+ {
313
+ _eventDrivers . Remove ( key ) ;
314
+ }
315
+ else
316
+ {
317
+ for ( var i = 0 ; i < driversForKey . Count ; ++ i )
318
+ {
319
+ var driver = driversForKey [ i ] ;
320
+ if ( driver . ValueNode . Tag == animatedValueTag )
321
+ {
322
+ driversForKey . RemoveAt ( i ) ;
323
+ break ;
324
+ }
325
+ }
326
+ }
327
+ }
292
328
}
293
329
294
330
public bool OnEventDispatch ( Event @event )
@@ -308,11 +344,17 @@ public bool OnEventDispatch(Event @event)
308
344
eventName = customEventName ;
309
345
}
310
346
311
- var eventDriver = default ( EventAnimationDriver ) ;
312
- if ( _eventDrivers . TryGetValue ( Tuple . Create ( @event . ViewTag , eventName ) , out eventDriver ) )
347
+ var driversForKey = default ( IList < EventAnimationDriver > ) ;
348
+ if ( _eventDrivers . TryGetValue ( Tuple . Create ( @event . ViewTag , eventName ) , out driversForKey ) )
313
349
{
314
- @event . Dispatch ( eventDriver ) ;
315
- _updatedNodes . Add ( eventDriver . ValueNode ) ;
350
+ foreach ( var driver in driversForKey )
351
+ {
352
+ @event . Dispatch ( driver ) ;
353
+ _runUpdateNodeList . Add ( driver . ValueNode ) ;
354
+ }
355
+
356
+ UpdateNodes ( _runUpdateNodeList ) ;
357
+ _runUpdateNodeList . Clear ( ) ;
316
358
return true ;
317
359
}
318
360
}
@@ -341,9 +383,64 @@ public bool OnEventDispatch(Event @event)
341
383
public void RunUpdates ( TimeSpan renderingTime )
342
384
{
343
385
DispatcherHelpers . AssertOnDispatcher ( ) ;
386
+ var hasFinishedAnimations = false ;
387
+
388
+ for ( var i = 0 ; i < _updatedNodes . Count ; ++ i )
389
+ {
390
+ var node = _updatedNodes [ i ] ;
391
+ _runUpdateNodeList . Add ( node ) ;
392
+ }
393
+
394
+ // Clean _updatedNodes queue
395
+ _updatedNodes . Clear ( ) ;
396
+
397
+ for ( var i = 0 ; i < _activeAnimations . Count ; ++ i )
398
+ {
399
+ var animation = _activeAnimations [ i ] ;
400
+ animation . RunAnimationStep ( renderingTime ) ;
401
+ var valueNode = animation . AnimatedValue ;
402
+ _runUpdateNodeList . Add ( valueNode ) ;
403
+ if ( animation . HasFinished )
404
+ {
405
+ hasFinishedAnimations = true ;
406
+ }
407
+ }
408
+
409
+ UpdateNodes ( _runUpdateNodeList ) ;
410
+ _runUpdateNodeList . Clear ( ) ;
411
+
412
+ // Cleanup finished animations. Iterate over the array of animations and override ones that has
413
+ // finished, then resize `_activeAnimations`.
414
+ if ( hasFinishedAnimations )
415
+ {
416
+ int dest = 0 ;
417
+ for ( var i = 0 ; i < _activeAnimations . Count ; ++ i )
418
+ {
419
+ var animation = _activeAnimations [ i ] ;
420
+ if ( ! animation . HasFinished )
421
+ {
422
+ _activeAnimations [ dest ++ ] = animation ;
423
+ }
424
+ else
425
+ {
426
+ animation . EndCallback . Invoke ( new JObject
427
+ {
428
+ { "finished" , true } ,
429
+ } ) ;
430
+ }
431
+ }
432
+
433
+ for ( var i = _activeAnimations . Count - 1 ; i >= dest ; -- i )
434
+ {
435
+ _activeAnimations . RemoveAt ( i ) ;
436
+ }
437
+ }
438
+ }
439
+
440
+ private void UpdateNodes ( IList < AnimatedNode > nodes )
441
+ {
344
442
var activeNodesCount = 0 ;
345
443
var updatedNodesCount = 0 ;
346
- var hasFinishedAnimations = false ;
347
444
348
445
// STEP 1.
349
446
// BFS over graph of nodes starting from ones from `_updatedNodes` and ones that are attached to
@@ -352,9 +449,14 @@ public void RunUpdates(TimeSpan renderingTime)
352
449
// animations as a part of this step.
353
450
354
451
_animatedGraphBFSColor ++ ; /* use new color */
452
+ if ( _animatedGraphBFSColor == AnimatedNode . InitialBfsColor )
453
+ {
454
+ // value "0" is used as an initial color for a new node, using it in BFS may cause some nodes to be skipped.
455
+ _animatedGraphBFSColor ++ ;
456
+ }
355
457
356
458
var nodesQueue = new Queue < AnimatedNode > ( ) ;
357
- foreach ( var node in _updatedNodes )
459
+ foreach ( var node in nodes )
358
460
{
359
461
if ( node . BfsColor != _animatedGraphBFSColor )
360
462
{
@@ -364,23 +466,6 @@ public void RunUpdates(TimeSpan renderingTime)
364
466
}
365
467
}
366
468
367
- foreach ( var animation in _activeAnimations )
368
- {
369
- animation . RunAnimationStep ( renderingTime ) ;
370
- var valueNode = animation . AnimatedValue ;
371
- if ( valueNode . BfsColor != _animatedGraphBFSColor )
372
- {
373
- valueNode . BfsColor = _animatedGraphBFSColor ;
374
- activeNodesCount ++ ;
375
- nodesQueue . Enqueue ( valueNode ) ;
376
- }
377
-
378
- if ( animation . HasFinished )
379
- {
380
- hasFinishedAnimations = true ;
381
- }
382
- }
383
-
384
469
while ( nodesQueue . Count > 0 )
385
470
{
386
471
var nextNode = nodesQueue . Dequeue ( ) ;
@@ -408,10 +493,16 @@ public void RunUpdates(TimeSpan renderingTime)
408
493
// step). We store number of visited nodes in this step in `updatedNodesCount`
409
494
410
495
_animatedGraphBFSColor ++ ;
496
+ if ( _animatedGraphBFSColor == AnimatedNode . InitialBfsColor )
497
+ {
498
+ // see reasoning for this check a few lines above
499
+ _animatedGraphBFSColor ++ ;
500
+ }
501
+
411
502
412
503
// find nodes with zero "incoming nodes", those can be either nodes from `mUpdatedNodes` or
413
504
// ones connected to active animations
414
- foreach ( var node in _updatedNodes )
505
+ foreach ( var node in nodes )
415
506
{
416
507
if ( node . ActiveIncomingNodes == 0 && node . BfsColor != _animatedGraphBFSColor )
417
508
{
@@ -421,17 +512,6 @@ public void RunUpdates(TimeSpan renderingTime)
421
512
}
422
513
}
423
514
424
- foreach ( var animation in _activeAnimations )
425
- {
426
- var valueNode = animation . AnimatedValue ;
427
- if ( valueNode . ActiveIncomingNodes == 0 && valueNode . BfsColor != _animatedGraphBFSColor )
428
- {
429
- valueNode . BfsColor = _animatedGraphBFSColor ;
430
- updatedNodesCount ++ ;
431
- nodesQueue . Enqueue ( valueNode ) ;
432
- }
433
- }
434
-
435
515
// Run main "update" loop
436
516
while ( nodesQueue . Count > 0 )
437
517
{
@@ -474,36 +554,6 @@ public void RunUpdates(TimeSpan renderingTime)
474
554
throw new InvalidOperationException (
475
555
Invariant ( $ "Looks like animated nodes graph has cycles, there are { activeNodesCount } but visited only { updatedNodesCount } .") ) ;
476
556
}
477
-
478
- // Clean _updatedNodes queue
479
- _updatedNodes . Clear ( ) ;
480
-
481
- // Cleanup finished animations. Iterate over the array of animations and override ones that has
482
- // finished, then resize `_activeAnimations`.
483
- if ( hasFinishedAnimations )
484
- {
485
- int dest = 0 ;
486
- for ( var i = 0 ; i < _activeAnimations . Count ; ++ i )
487
- {
488
- var animation = _activeAnimations [ i ] ;
489
- if ( ! animation . HasFinished )
490
- {
491
- _activeAnimations [ dest ++ ] = animation ;
492
- }
493
- else
494
- {
495
- animation . EndCallback . Invoke ( new JObject
496
- {
497
- { "finished" , true } ,
498
- } ) ;
499
- }
500
- }
501
-
502
- for ( var i = _activeAnimations . Count - 1 ; i >= dest ; -- i )
503
- {
504
- _activeAnimations . RemoveAt ( i ) ;
505
- }
506
- }
507
557
}
508
558
509
559
internal AnimatedNode GetNodeById ( int tag )
0 commit comments