Skip to content

Commit 390bbda

Browse files
committed
fix(NativeAnimated): Support multiple events attached to the same prop
Copied fix from facebook/react-native@921b9ac.
1 parent e9d560a commit 390bbda

File tree

3 files changed

+124
-71
lines changed

3 files changed

+124
-71
lines changed

ReactWindows/ReactNative.Shared/Animated/AnimatedNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ namespace ReactNative.Animated
44
{
55
abstract class AnimatedNode
66
{
7+
public const int InitialBfsColor = 0;
8+
79
private List<AnimatedNode> _children;
810

911
public AnimatedNode(int tag)

ReactWindows/ReactNative.Shared/Animated/NativeAnimatedModule.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,12 @@ public void addAnimatedEventToView(int viewTag, string eventName, JObject eventM
354354
/// </summary>
355355
/// <param name="viewTag">The view tag.</param>
356356
/// <param name="eventName">The event name.</param>
357+
/// <param name="animatedValueTag">The value tag.</param>
357358
[ReactMethod]
358-
public void removeAnimatedEventFromView(int viewTag, string eventName)
359+
public void removeAnimatedEventFromView(int viewTag, string eventName, int animatedValueTag)
359360
{
360361
_operations.Add(manager =>
361-
manager.RemoveAnimatedEventFromView(viewTag, eventName));
362+
manager.RemoveAnimatedEventFromView(viewTag, eventName, animatedValueTag));
362363
}
363364
}
364365
}

ReactWindows/ReactNative.Shared/Animated/NativeAnimatedNodesManager.cs

Lines changed: 119 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using ReactNative.UIManager.Events;
55
using System;
66
using System.Collections.Generic;
7+
using System.Linq;
78
using static System.FormattableString;
89

910
namespace ReactNative.Animated
@@ -31,9 +32,14 @@ class NativeAnimatedNodesManager : IEventDispatcherListener
3132
private readonly IDictionary<int, AnimatedNode> _animatedNodes = new Dictionary<int, AnimatedNode>();
3233
private readonly IList<AnimationDriver> _activeAnimations = new List<AnimationDriver>();
3334
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>>();
3539
private readonly IReadOnlyDictionary<string, object> _customEventTypes;
3640
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>();
3743

3844
private int _animatedGraphBFSColor = 0;
3945

@@ -283,12 +289,42 @@ public void AddAnimatedEventToView(int viewTag, string eventName, JObject eventM
283289

284290
var pathList = eventMapping["nativeEventPath"].ToObject<string[]>();
285291
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+
}
287303
}
288304

289-
public void RemoveAnimatedEventFromView(int viewTag, string eventName)
305+
public void RemoveAnimatedEventFromView(int viewTag, string eventName, int animatedValueTag)
290306
{
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+
}
292328
}
293329

294330
public bool OnEventDispatch(Event @event)
@@ -308,11 +344,17 @@ public bool OnEventDispatch(Event @event)
308344
eventName = customEventName;
309345
}
310346

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))
313349
{
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();
316358
return true;
317359
}
318360
}
@@ -341,9 +383,64 @@ public bool OnEventDispatch(Event @event)
341383
public void RunUpdates(TimeSpan renderingTime)
342384
{
343385
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+
{
344442
var activeNodesCount = 0;
345443
var updatedNodesCount = 0;
346-
var hasFinishedAnimations = false;
347444

348445
// STEP 1.
349446
// 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)
352449
// animations as a part of this step.
353450

354451
_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+
}
355457

356458
var nodesQueue = new Queue<AnimatedNode>();
357-
foreach (var node in _updatedNodes)
459+
foreach (var node in nodes)
358460
{
359461
if (node.BfsColor != _animatedGraphBFSColor)
360462
{
@@ -364,23 +466,6 @@ public void RunUpdates(TimeSpan renderingTime)
364466
}
365467
}
366468

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-
384469
while (nodesQueue.Count > 0)
385470
{
386471
var nextNode = nodesQueue.Dequeue();
@@ -408,10 +493,16 @@ public void RunUpdates(TimeSpan renderingTime)
408493
// step). We store number of visited nodes in this step in `updatedNodesCount`
409494

410495
_animatedGraphBFSColor++;
496+
if (_animatedGraphBFSColor == AnimatedNode.InitialBfsColor)
497+
{
498+
// see reasoning for this check a few lines above
499+
_animatedGraphBFSColor++;
500+
}
501+
411502

412503
// find nodes with zero "incoming nodes", those can be either nodes from `mUpdatedNodes` or
413504
// ones connected to active animations
414-
foreach (var node in _updatedNodes)
505+
foreach (var node in nodes)
415506
{
416507
if (node.ActiveIncomingNodes == 0 && node.BfsColor != _animatedGraphBFSColor)
417508
{
@@ -421,17 +512,6 @@ public void RunUpdates(TimeSpan renderingTime)
421512
}
422513
}
423514

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-
435515
// Run main "update" loop
436516
while (nodesQueue.Count > 0)
437517
{
@@ -474,36 +554,6 @@ public void RunUpdates(TimeSpan renderingTime)
474554
throw new InvalidOperationException(
475555
Invariant($"Looks like animated nodes graph has cycles, there are {activeNodesCount} but visited only {updatedNodesCount}."));
476556
}
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-
}
507557
}
508558

509559
internal AnimatedNode GetNodeById(int tag)

0 commit comments

Comments
 (0)