Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Side by Side Diff: ios/chrome/browser/ui/reversed_animation.mm

Issue 802633007: Upstream iOS UI utilities. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove rand() Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/chrome/browser/ui/reversed_animation.h ('k') | ios/chrome/browser/ui/snapshots_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ios/chrome/browser/ui/reversed_animation.h"
6
7 #import <QuartzCore/QuartzCore.h>
8 #include <algorithm>
9 #include <cmath>
10
11 #include "base/logging.h"
12 #include "base/mac/objc_property_releaser.h"
13
14 @protocol ReversedAnimationProtocol;
15 typedef CAAnimation<ReversedAnimationProtocol> ReversedAnimation;
16
17 namespace {
18 // Enum type used to denote the direction of a reversed animation relative to
19 // the original animation's direction.
20 typedef enum {
21 ANIMATION_DIRECTION_NORMAL,
22 ANIMATION_DIRECTION_REVERSE
23 } AnimationDirection;
24 // Returns the AnimationDirection opposite of |direction|.
25 AnimationDirection AnimationDirectionOpposite(AnimationDirection direction) {
26 return direction == ANIMATION_DIRECTION_NORMAL ? ANIMATION_DIRECTION_REVERSE
27 : ANIMATION_DIRECTION_NORMAL;
28 }
29 } // namespace
30
31 // Returns an animation that reverses |animation| when added to |layer|, given
32 // that |animation| is in |parent|'s timespace, which begins at
33 // |parentBeginTime|.
34 CAAnimation* CAAnimationMakeReverse(CAAnimation* animation,
35 CALayer* layer,
36 CAAnimationGroup* parent,
37 CFTimeInterval parentBeginTime);
38 // Updates |reversedAnimation|'s properties for |animationToReverse|, given that
39 // |animationToReverse| is in |parent|'s timespace, which begins at
40 // |parentBeginTime|.
41 void UpdateReversedAnimation(ReversedAnimation* reversedAnimation,
42 CAAnimation* animationToReverse,
43 CALayer* layer,
44 CAAnimationGroup* parent,
45 CFTimeInterval parentBeginTime);
46
47 #pragma mark - ReversedAnimation protocol
48
49 @protocol ReversedAnimationProtocol<NSObject>
50
51 // The original animation that's being played in reverse.
52 @property(nonatomic, retain) CAAnimation* originalAnimation;
53 // The current direction for the animation.
54 @property(nonatomic, assign) AnimationDirection animationDirection;
55 // The offset into the original animation's duration at the begining of the
56 // reverse animation.
57 @property(nonatomic, assign) CFTimeInterval animationTimeOffset;
58
59 @end
60
61 #pragma mark - ReversedBasicAnimation
62
63 @interface ReversedBasicAnimation
64 : CABasicAnimation<ReversedAnimationProtocol> {
65 base::mac::ObjCPropertyReleaser _propertyReleaser_ReversedBasicAnimation;
66 }
67
68 // Returns an animation that performs |animation| in reverse when added to
69 // |layer|. |parentBeginTime| should be set to the beginTime in absolute time
70 // of |animation|'s parent in the timing hierarchy.
71 + (instancetype)reversedAnimationForAnimation:(CABasicAnimation*)animation
72 forLayer:(CALayer*)layer
73 parent:(CAAnimationGroup*)parent
74 parentBeginTime:(CFTimeInterval)parentBeginTime;
75
76 @end
77
78 @implementation ReversedBasicAnimation
79
80 @synthesize originalAnimation = _originalAnimation;
81 @synthesize animationDirection = _animationDirection;
82 @synthesize animationTimeOffset = _animationTimeOffset;
83
84 - (instancetype)init {
85 self = [super init];
86 if (self) {
87 _propertyReleaser_ReversedBasicAnimation.Init(
88 self, [ReversedBasicAnimation class]);
89 }
90 return self;
91 }
92
93 - (instancetype)copyWithZone:(NSZone*)zone {
94 ReversedBasicAnimation* copy = [super copyWithZone:zone];
95 copy.originalAnimation = self.originalAnimation;
96 copy.animationDirection = self.animationDirection;
97 copy.animationTimeOffset = self.animationTimeOffset;
98 return copy;
99 }
100
101 + (instancetype)reversedAnimationForAnimation:(CABasicAnimation*)animation
102 forLayer:(CALayer*)layer
103 parent:(CAAnimationGroup*)parent
104 parentBeginTime:(CFTimeInterval)parentBeginTime {
105 // Create new animation and copy properties. Note that we can't use |-copy|
106 // because we need the new animation to be the correct class.
107 NSString* keyPath = animation.keyPath;
108 CFTimeInterval now =
109 [layer convertTime:CACurrentMediaTime() fromLayer:nil] - parentBeginTime;
110 ReversedBasicAnimation* reversedAnimation =
111 [ReversedBasicAnimation animationWithKeyPath:keyPath];
112 UpdateReversedAnimation(reversedAnimation, animation, layer, parent,
113 parentBeginTime);
114
115 // Update from and to values.
116 BOOL isReversed =
117 reversedAnimation.animationDirection == ANIMATION_DIRECTION_REVERSE;
118 CABasicAnimation* originalBasicAnimation =
119 static_cast<CABasicAnimation*>(reversedAnimation.originalAnimation);
120 reversedAnimation.toValue = isReversed ? originalBasicAnimation.fromValue
121 : originalBasicAnimation.toValue;
122 if (now > animation.beginTime &&
123 now < animation.beginTime + animation.duration) {
124 // Use the presentation layer's current value for reversals that occur mid-
125 // animation.
126 reversedAnimation.fromValue =
127 [[layer presentationLayer] valueForKeyPath:keyPath];
128 } else {
129 reversedAnimation.fromValue = isReversed ? originalBasicAnimation.toValue
130 : originalBasicAnimation.fromValue;
131 }
132 return reversedAnimation;
133 }
134
135 @end
136
137 #pragma mark - ReversedAnimationGroup
138
139 @interface ReversedAnimationGroup
140 : CAAnimationGroup<ReversedAnimationProtocol> {
141 base::mac::ObjCPropertyReleaser _propertyReleaser_ReversedAnimationGroup;
142 }
143
144 // Returns an animation that performs |animation| in reverse when added to
145 // |layer|. |parentBeginTime| should be set to the beginTime in absolute time
146 // of the animation group to which |animation| belongs.
147 + (instancetype)reversedAnimationGroupForGroup:(CAAnimationGroup*)group
148 forLayer:(CALayer*)layer
149 parent:(CAAnimationGroup*)parent
150 parentBeginTime:(CFTimeInterval)parentBeginTime;
151
152 @end
153
154 @implementation ReversedAnimationGroup
155
156 @synthesize originalAnimation = _originalAnimation;
157 @synthesize animationDirection = _animationDirection;
158 @synthesize animationTimeOffset = _animationTimeOffset;
159
160 - (instancetype)init {
161 self = [super init];
162 if (self) {
163 _propertyReleaser_ReversedAnimationGroup.Init(
164 self, [ReversedAnimationGroup class]);
165 }
166 return self;
167 }
168
169 - (instancetype)copyWithZone:(NSZone*)zone {
170 ReversedAnimationGroup* copy = [super copyWithZone:zone];
171 copy.originalAnimation = self.originalAnimation;
172 copy.animationDirection = self.animationDirection;
173 copy.animationTimeOffset = self.animationTimeOffset;
174 return copy;
175 }
176
177 + (instancetype)reversedAnimationGroupForGroup:(CAAnimationGroup*)group
178 forLayer:(CALayer*)layer
179 parent:(CAAnimationGroup*)parent
180 parentBeginTime:(CFTimeInterval)parentBeginTime {
181 // Create new animation and copy properties. Note that we can't use |-copy|
182 // because we need the new animation to be the correct class.
183 ReversedAnimationGroup* reversedGroup = [ReversedAnimationGroup animation];
184 UpdateReversedAnimation(reversedGroup, group, layer, parent, parentBeginTime);
185
186 // Reverse the animations of the original group.
187 NSMutableArray* reversedAnimations = [NSMutableArray array];
188 for (CAAnimation* animation in group.animations) {
189 CAAnimation* reversedAnimation = CAAnimationMakeReverse(
190 animation, layer, group, group.beginTime + parentBeginTime);
191 [reversedAnimations addObject:reversedAnimation];
192 }
193 reversedGroup.animations = reversedAnimations;
194
195 return reversedGroup;
196 }
197
198 @end
199
200 #pragma mark - animation_util functions
201
202 CAAnimation* CAAnimationMakeReverse(CAAnimation* animation, CALayer* layer) {
203 return CAAnimationMakeReverse(animation, layer, nil, layer.beginTime);
204 }
205
206 CAAnimation* CAAnimationMakeReverse(CAAnimation* animation,
207 CALayer* layer,
208 CAAnimationGroup* parent,
209 CFTimeInterval parentBeginTime) {
210 CAAnimation* reversedAnimation = nil;
211 if ([animation isKindOfClass:[CABasicAnimation class]]) {
212 CABasicAnimation* basicAnimation =
213 static_cast<CABasicAnimation*>(animation);
214 reversedAnimation =
215 [ReversedBasicAnimation reversedAnimationForAnimation:basicAnimation
216 forLayer:layer
217 parent:parent
218 parentBeginTime:parentBeginTime];
219 } else if ([animation isKindOfClass:[CAAnimationGroup class]]) {
220 CAAnimationGroup* animationGroup =
221 static_cast<CAAnimationGroup*>(animation);
222 reversedAnimation =
223 [ReversedAnimationGroup reversedAnimationGroupForGroup:animationGroup
224 forLayer:layer
225 parent:parent
226 parentBeginTime:parentBeginTime];
227 } else {
228 // TODO(kkhorimoto): Investigate possible general-case reversals. It may
229 // be possible to implement this by manipulating the CAMediaTiming
230 // properties.
231 }
232 return reversedAnimation;
233 }
234
235 void UpdateReversedAnimation(ReversedAnimation* reversedAnimation,
236 CAAnimation* animationToReverse,
237 CALayer* layer,
238 CAAnimationGroup* parent,
239 CFTimeInterval parentBeginTime) {
240 // Copy properties.
241 CFTimeInterval now =
242 [layer convertTime:CACurrentMediaTime() fromLayer:nil] - parentBeginTime;
243 reversedAnimation.fillMode = animationToReverse.fillMode;
244 reversedAnimation.removedOnCompletion =
245 animationToReverse.removedOnCompletion;
246 reversedAnimation.timingFunction = animationToReverse.timingFunction;
247
248 // Extract the previous reversal if it exists.
249 ReversedAnimation* previousReversedAnimation = nil;
250 Protocol* reversedAnimationProtocol = @protocol(ReversedAnimationProtocol);
251 if ([animationToReverse conformsToProtocol:reversedAnimationProtocol]) {
252 previousReversedAnimation =
253 static_cast<ReversedAnimation*>(animationToReverse);
254 animationToReverse = previousReversedAnimation.originalAnimation;
255 }
256 reversedAnimation.originalAnimation = animationToReverse;
257 reversedAnimation.animationDirection =
258 previousReversedAnimation
259 ? AnimationDirectionOpposite(
260 previousReversedAnimation.animationDirection)
261 : ANIMATION_DIRECTION_REVERSE;
262
263 CAAnimation* previousAnimation = previousReversedAnimation
264 ? previousReversedAnimation
265 : animationToReverse;
266 BOOL isReversed =
267 reversedAnimation.animationDirection == ANIMATION_DIRECTION_REVERSE;
268 if (now < previousAnimation.beginTime) {
269 // Reversal occurs before previous animation begins.
270 reversedAnimation.beginTime = 2.0 * now - previousAnimation.beginTime -
271 reversedAnimation.originalAnimation.duration;
272 reversedAnimation.duration = reversedAnimation.originalAnimation.duration;
273 reversedAnimation.animationTimeOffset =
274 isReversed ? 0 : reversedAnimation.originalAnimation.duration;
275 } else if (now < previousAnimation.beginTime + previousAnimation.duration) {
276 // Reversal occurs while the previous animation is occurring.
277 reversedAnimation.beginTime = 0;
278 CFTimeInterval timeDelta = now - previousAnimation.beginTime;
279 reversedAnimation.animationTimeOffset =
280 previousReversedAnimation.animationTimeOffset +
281 (isReversed ? 1.0 : -1.0) * timeDelta;
282 reversedAnimation.duration =
283 isReversed ? reversedAnimation.animationTimeOffset
284 : reversedAnimation.originalAnimation.duration -
285 reversedAnimation.animationTimeOffset;
286 } else {
287 // Reversal occurs after the previous animation has ended.
288 if (!parentBeginTime) {
289 // If the parent's begin time is zero, the animation is using absolute
290 // time as its beginTime.
291 reversedAnimation.beginTime =
292 2.0 * now - previousAnimation.beginTime - previousAnimation.duration;
293 } else {
294 CFTimeInterval previousEndTime =
295 previousAnimation.beginTime + previousAnimation.duration;
296 if (now > parent.duration) {
297 // The animation's parent has already ended, so use the difference
298 // between the parent's ending and the previous animation's end.
299 reversedAnimation.beginTime = parent.duration - previousEndTime;
300 } else {
301 // The parent hasn't ended, so use the difference between the current
302 // time and the previous animation's end.
303 reversedAnimation.beginTime = now - previousEndTime;
304 }
305 }
306 reversedAnimation.duration = reversedAnimation.originalAnimation.duration;
307 reversedAnimation.animationTimeOffset =
308 isReversed ? reversedAnimation.originalAnimation.duration : 0;
309 }
310 }
311
312 void ReverseAnimationsForKeyForLayers(NSString* key, NSArray* layers) {
313 for (CALayer* layer in layers) {
314 CAAnimation* reversedAnimation =
315 CAAnimationMakeReverse([layer animationForKey:key], layer);
316 [layer removeAnimationForKey:key];
317 [layer addAnimation:reversedAnimation forKey:key];
318 }
319 }
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/reversed_animation.h ('k') | ios/chrome/browser/ui/snapshots_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698