Skip to content

Commit

Permalink
fix(ios): simultaneous animationSets desynchronize over time
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwcg committed Jun 28, 2024
1 parent 9be2145 commit 428e473
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 7 deletions.
35 changes: 35 additions & 0 deletions ios/sdk/module/animation2/HippyNextAnimationGroup.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,26 @@ @implementation HippyNextAnimationGroup
{
NSInteger _currentRepeatCount;
BOOL _isGroupPausedCausedReturn;

// Member variables used to correct the animation time.
CFTimeInterval _totalDuration;
CFTimeInterval _lastStartTime;
CFTimeInterval _cumulativeFrameDelay;
}

- (BOOL)prepareForTarget:(id)target withType:(NSString *)type {
CFTimeInterval totalDuration = 0.0;
HippyNextAnimation *previousAnimation;

for (HippyNextAnimation *anim in self.animations) {
if (![anim prepareForTarget:target withType:type]) {
return NO;
}
if (!previousAnimation || (previousAnimation && anim.isFollow)) {
totalDuration += anim.duration;
}
previousAnimation = anim;
_totalDuration = totalDuration;
}
return YES;
}
Expand Down Expand Up @@ -93,6 +106,28 @@ - (void)startAnimationWithRepeatCount:(NSUInteger)repeatCount {
}
}];
} else {
// Record the time when the animation group started,
// and correct the time offset if needed.
if (!previousAnimation) {
if (_lastStartTime > DBL_EPSILON) {
// Since CADisplayLink's callback is used to execute the animation group,
// there is a frame time interval between each animation.
// In order to ensure the time synchronization between different animation groups,
// we need to continuously correct possible time deviations to avoid the accumulation of time differences.
CFTimeInterval refreshPeriod = HPOPAnimator.sharedAnimator.refreshPeriod;
if (refreshPeriod > DBL_EPSILON) {
if (_cumulativeFrameDelay <= DBL_EPSILON) {
for (HippyNextAnimation *animation in self.animations) {
_cumulativeFrameDelay += ceil(animation.duration / refreshPeriod) * refreshPeriod - animation.duration;
}
}

CFTimeInterval timeOffset = (CACurrentMediaTime() - _lastStartTime) - (_totalDuration + _cumulativeFrameDelay);
animation.beginTime = timeOffset;
}
}
_lastStartTime = CACurrentMediaTime();
}
[animation startAnimation];
}
previousAnimation = animation;
Expand Down
47 changes: 40 additions & 7 deletions ios/sdk/module/animation2/pop/HPOPAnimator.mm
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,20 @@ - (void)_scheduleProcessPendingList
pthread_mutex_unlock(&_lock);
}

// Custom hash function for dispatch_queue_t
struct DispatchQueueHash {
std::size_t operator()(const dispatch_queue_t& queue) const {
return std::hash<void*>()((__bridge void*)queue);
}
};

// Custom equality function for dispatch_queue_t
struct DispatchQueueEqual {
bool operator()(const dispatch_queue_t& lhs, const dispatch_queue_t& rhs) const {
return lhs == rhs;
}
};

- (void)_renderTime:(CFTimeInterval)time items:(std::list<POPAnimatorItemRef> &)items
{
// begin transaction with actions disabled
Expand Down Expand Up @@ -559,14 +573,33 @@ - (void)_renderTime:(CFTimeInterval)time items:(std::list<POPAnimatorItemRef> &)
// unlock
pthread_mutex_unlock(&_lock);

for (auto item : vector) {
if (dispatch_queue_t queue = item->animation.customRunningQueue) {
dispatch_async(queue, ^{
[self _renderTime:time item:item];
});
} else {
[self _renderTime:time item:item];
// Group items by their customRunningQueue
std::unordered_map<dispatch_queue_t, std::vector<POPAnimatorItemRef>, DispatchQueueHash, DispatchQueueEqual> queueMap;
for (auto& item : vector) {
dispatch_queue_t queue = item->animation.customRunningQueue;
queueMap[queue].emplace_back(std::move(item));
}

// Dispatch tasks for each queue
for (auto& pair : queueMap) {
dispatch_queue_t queue = pair.first;
auto itemList = pair.second; // Make a copy of the item list to capture in the block

if (queue) {
__weak __typeof(self) weakSelf = self;
dispatch_async(queue, ^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
for (auto& item : itemList) {
[strongSelf _renderTime:time item:item];
}
}
});
} else {
for (auto& item : itemList) {
[self _renderTime:time item:item];
}
}
}
}

Expand Down

0 comments on commit 428e473

Please sign in to comment.