Skip to content

Commit b0d0e51

Browse files
p-sunfacebook-github-bot
authored andcommitted
iOS: Animated image should animate at the same speed regardless of framerate
Summary: In iOS 11, [CADisplayLink](https://developer.apple.com/documentation/quartzcore/cadisplaylink)'s frameInterval was deprecated in favor of preferredFramesPerSecond, but these two properties have different underlying assumptions. - set frameInterval to 2 for 30fps - set preferredFramesPerSecond to 30 for 30fps. When you use preferredFramesPerSecond, assume frameInterval is 1. This fix ensures gifs in <Image> component will animate at same speed regardless of framerate. Reviewed By: shergin Differential Revision: D21414014 fbshipit-source-id: 40ab23bab1990cf65d2802830b6835f350999537
1 parent 530dffa commit b0d0e51

File tree

1 file changed

+11
-15
lines changed

1 file changed

+11
-15
lines changed

Libraries/Image/RCTUIImageViewAnimated.m

+11-15
Original file line numberDiff line numberDiff line change
@@ -181,22 +181,17 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
181181
{
182182
#if TARGET_OS_UIKITFORMAC
183183
// TODO: `displayLink.frameInterval` is not available on UIKitForMac
184-
NSTimeInterval duration = displayLink.duration;
184+
NSTimeInterval durationToNextRefresh = displayLink.duration;
185185
#else
186-
NSTimeInterval duration = displayLink.duration;
186+
// displaylink.duration -- time interval between frames, assuming maximumFramesPerSecond
187+
// displayLink.preferredFramesPerSecond (>= iOS 10) -- Set to 30 for displayDidRefresh to be called at 30 fps
188+
// displayLink.frameInterval (< iOS 10) -- # of frames that must pass before each displayDidRefresh. After iOS 10, when this is set to 2, preferredFramesPerSecond becomes 30 fps.
189+
// durationToNextRefresh -- Time interval to the next time displayDidRefresh is called
190+
NSTimeInterval durationToNextRefresh;
187191
if (@available(iOS 10.0, *)) {
188-
// Per https://developer.apple.com/documentation/quartzcore/cadisplaylink
189-
// displayLink.duration provides the amount of time between frames at the maximumFramesPerSecond
190-
// Thus we need to calculate true duration based on preferredFramesPerSecond
191-
if (displayLink.preferredFramesPerSecond != 0) {
192-
double maxFrameRate = 60.0; // default to 60 fps
193-
if (@available(iOS 10.3, tvOS 10.3, *)) {
194-
maxFrameRate = self.window.screen.maximumFramesPerSecond;
195-
}
196-
duration = duration * displayLink.preferredFramesPerSecond / maxFrameRate;
197-
} // else respect maximumFramesPerSecond
198-
} else { // version < (ios 10)
199-
duration = duration * displayLink.frameInterval;
192+
durationToNextRefresh = displayLink.targetTimestamp - displayLink.timestamp;
193+
} else {
194+
durationToNextRefresh = displayLink.duration * displayLink.frameInterval;
200195
}
201196
#endif
202197
NSUInteger totalFrameCount = self.totalFrameCount;
@@ -206,13 +201,14 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
206201
// Check if we have the frame buffer firstly to improve performance
207202
if (!self.bufferMiss) {
208203
// Then check if timestamp is reached
209-
self.currentTime += duration;
204+
self.currentTime += durationToNextRefresh;
210205
NSTimeInterval currentDuration = [self.animatedImage animatedImageDurationAtIndex:currentFrameIndex];
211206
if (self.currentTime < currentDuration) {
212207
// Current frame timestamp not reached, return
213208
return;
214209
}
215210
self.currentTime -= currentDuration;
211+
// nextDuration - duration to wait before displaying next image
216212
NSTimeInterval nextDuration = [self.animatedImage animatedImageDurationAtIndex:nextFrameIndex];
217213
if (self.currentTime > nextDuration) {
218214
// Do not skip frame

0 commit comments

Comments
 (0)