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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/chrome/browser/ui/reversed_animation.mm
diff --git a/ios/chrome/browser/ui/reversed_animation.mm b/ios/chrome/browser/ui/reversed_animation.mm
new file mode 100644
index 0000000000000000000000000000000000000000..a46b0dcdd4954209e95340119ebe24a8e658cfcc
--- /dev/null
+++ b/ios/chrome/browser/ui/reversed_animation.mm
@@ -0,0 +1,319 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/ui/reversed_animation.h"
+
+#import <QuartzCore/QuartzCore.h>
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+
+@protocol ReversedAnimationProtocol;
+typedef CAAnimation<ReversedAnimationProtocol> ReversedAnimation;
+
+namespace {
+// Enum type used to denote the direction of a reversed animation relative to
+// the original animation's direction.
+typedef enum {
+ ANIMATION_DIRECTION_NORMAL,
+ ANIMATION_DIRECTION_REVERSE
+} AnimationDirection;
+// Returns the AnimationDirection opposite of |direction|.
+AnimationDirection AnimationDirectionOpposite(AnimationDirection direction) {
+ return direction == ANIMATION_DIRECTION_NORMAL ? ANIMATION_DIRECTION_REVERSE
+ : ANIMATION_DIRECTION_NORMAL;
+}
+} // namespace
+
+// Returns an animation that reverses |animation| when added to |layer|, given
+// that |animation| is in |parent|'s timespace, which begins at
+// |parentBeginTime|.
+CAAnimation* CAAnimationMakeReverse(CAAnimation* animation,
+ CALayer* layer,
+ CAAnimationGroup* parent,
+ CFTimeInterval parentBeginTime);
+// Updates |reversedAnimation|'s properties for |animationToReverse|, given that
+// |animationToReverse| is in |parent|'s timespace, which begins at
+// |parentBeginTime|.
+void UpdateReversedAnimation(ReversedAnimation* reversedAnimation,
+ CAAnimation* animationToReverse,
+ CALayer* layer,
+ CAAnimationGroup* parent,
+ CFTimeInterval parentBeginTime);
+
+#pragma mark - ReversedAnimation protocol
+
+@protocol ReversedAnimationProtocol<NSObject>
+
+// The original animation that's being played in reverse.
+@property(nonatomic, retain) CAAnimation* originalAnimation;
+// The current direction for the animation.
+@property(nonatomic, assign) AnimationDirection animationDirection;
+// The offset into the original animation's duration at the begining of the
+// reverse animation.
+@property(nonatomic, assign) CFTimeInterval animationTimeOffset;
+
+@end
+
+#pragma mark - ReversedBasicAnimation
+
+@interface ReversedBasicAnimation
+ : CABasicAnimation<ReversedAnimationProtocol> {
+ base::mac::ObjCPropertyReleaser _propertyReleaser_ReversedBasicAnimation;
+}
+
+// Returns an animation that performs |animation| in reverse when added to
+// |layer|. |parentBeginTime| should be set to the beginTime in absolute time
+// of |animation|'s parent in the timing hierarchy.
++ (instancetype)reversedAnimationForAnimation:(CABasicAnimation*)animation
+ forLayer:(CALayer*)layer
+ parent:(CAAnimationGroup*)parent
+ parentBeginTime:(CFTimeInterval)parentBeginTime;
+
+@end
+
+@implementation ReversedBasicAnimation
+
+@synthesize originalAnimation = _originalAnimation;
+@synthesize animationDirection = _animationDirection;
+@synthesize animationTimeOffset = _animationTimeOffset;
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _propertyReleaser_ReversedBasicAnimation.Init(
+ self, [ReversedBasicAnimation class]);
+ }
+ return self;
+}
+
+- (instancetype)copyWithZone:(NSZone*)zone {
+ ReversedBasicAnimation* copy = [super copyWithZone:zone];
+ copy.originalAnimation = self.originalAnimation;
+ copy.animationDirection = self.animationDirection;
+ copy.animationTimeOffset = self.animationTimeOffset;
+ return copy;
+}
+
++ (instancetype)reversedAnimationForAnimation:(CABasicAnimation*)animation
+ forLayer:(CALayer*)layer
+ parent:(CAAnimationGroup*)parent
+ parentBeginTime:(CFTimeInterval)parentBeginTime {
+ // Create new animation and copy properties. Note that we can't use |-copy|
+ // because we need the new animation to be the correct class.
+ NSString* keyPath = animation.keyPath;
+ CFTimeInterval now =
+ [layer convertTime:CACurrentMediaTime() fromLayer:nil] - parentBeginTime;
+ ReversedBasicAnimation* reversedAnimation =
+ [ReversedBasicAnimation animationWithKeyPath:keyPath];
+ UpdateReversedAnimation(reversedAnimation, animation, layer, parent,
+ parentBeginTime);
+
+ // Update from and to values.
+ BOOL isReversed =
+ reversedAnimation.animationDirection == ANIMATION_DIRECTION_REVERSE;
+ CABasicAnimation* originalBasicAnimation =
+ static_cast<CABasicAnimation*>(reversedAnimation.originalAnimation);
+ reversedAnimation.toValue = isReversed ? originalBasicAnimation.fromValue
+ : originalBasicAnimation.toValue;
+ if (now > animation.beginTime &&
+ now < animation.beginTime + animation.duration) {
+ // Use the presentation layer's current value for reversals that occur mid-
+ // animation.
+ reversedAnimation.fromValue =
+ [[layer presentationLayer] valueForKeyPath:keyPath];
+ } else {
+ reversedAnimation.fromValue = isReversed ? originalBasicAnimation.toValue
+ : originalBasicAnimation.fromValue;
+ }
+ return reversedAnimation;
+}
+
+@end
+
+#pragma mark - ReversedAnimationGroup
+
+@interface ReversedAnimationGroup
+ : CAAnimationGroup<ReversedAnimationProtocol> {
+ base::mac::ObjCPropertyReleaser _propertyReleaser_ReversedAnimationGroup;
+}
+
+// Returns an animation that performs |animation| in reverse when added to
+// |layer|. |parentBeginTime| should be set to the beginTime in absolute time
+// of the animation group to which |animation| belongs.
++ (instancetype)reversedAnimationGroupForGroup:(CAAnimationGroup*)group
+ forLayer:(CALayer*)layer
+ parent:(CAAnimationGroup*)parent
+ parentBeginTime:(CFTimeInterval)parentBeginTime;
+
+@end
+
+@implementation ReversedAnimationGroup
+
+@synthesize originalAnimation = _originalAnimation;
+@synthesize animationDirection = _animationDirection;
+@synthesize animationTimeOffset = _animationTimeOffset;
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _propertyReleaser_ReversedAnimationGroup.Init(
+ self, [ReversedAnimationGroup class]);
+ }
+ return self;
+}
+
+- (instancetype)copyWithZone:(NSZone*)zone {
+ ReversedAnimationGroup* copy = [super copyWithZone:zone];
+ copy.originalAnimation = self.originalAnimation;
+ copy.animationDirection = self.animationDirection;
+ copy.animationTimeOffset = self.animationTimeOffset;
+ return copy;
+}
+
++ (instancetype)reversedAnimationGroupForGroup:(CAAnimationGroup*)group
+ forLayer:(CALayer*)layer
+ parent:(CAAnimationGroup*)parent
+ parentBeginTime:(CFTimeInterval)parentBeginTime {
+ // Create new animation and copy properties. Note that we can't use |-copy|
+ // because we need the new animation to be the correct class.
+ ReversedAnimationGroup* reversedGroup = [ReversedAnimationGroup animation];
+ UpdateReversedAnimation(reversedGroup, group, layer, parent, parentBeginTime);
+
+ // Reverse the animations of the original group.
+ NSMutableArray* reversedAnimations = [NSMutableArray array];
+ for (CAAnimation* animation in group.animations) {
+ CAAnimation* reversedAnimation = CAAnimationMakeReverse(
+ animation, layer, group, group.beginTime + parentBeginTime);
+ [reversedAnimations addObject:reversedAnimation];
+ }
+ reversedGroup.animations = reversedAnimations;
+
+ return reversedGroup;
+}
+
+@end
+
+#pragma mark - animation_util functions
+
+CAAnimation* CAAnimationMakeReverse(CAAnimation* animation, CALayer* layer) {
+ return CAAnimationMakeReverse(animation, layer, nil, layer.beginTime);
+}
+
+CAAnimation* CAAnimationMakeReverse(CAAnimation* animation,
+ CALayer* layer,
+ CAAnimationGroup* parent,
+ CFTimeInterval parentBeginTime) {
+ CAAnimation* reversedAnimation = nil;
+ if ([animation isKindOfClass:[CABasicAnimation class]]) {
+ CABasicAnimation* basicAnimation =
+ static_cast<CABasicAnimation*>(animation);
+ reversedAnimation =
+ [ReversedBasicAnimation reversedAnimationForAnimation:basicAnimation
+ forLayer:layer
+ parent:parent
+ parentBeginTime:parentBeginTime];
+ } else if ([animation isKindOfClass:[CAAnimationGroup class]]) {
+ CAAnimationGroup* animationGroup =
+ static_cast<CAAnimationGroup*>(animation);
+ reversedAnimation =
+ [ReversedAnimationGroup reversedAnimationGroupForGroup:animationGroup
+ forLayer:layer
+ parent:parent
+ parentBeginTime:parentBeginTime];
+ } else {
+ // TODO(kkhorimoto): Investigate possible general-case reversals. It may
+ // be possible to implement this by manipulating the CAMediaTiming
+ // properties.
+ }
+ return reversedAnimation;
+}
+
+void UpdateReversedAnimation(ReversedAnimation* reversedAnimation,
+ CAAnimation* animationToReverse,
+ CALayer* layer,
+ CAAnimationGroup* parent,
+ CFTimeInterval parentBeginTime) {
+ // Copy properties.
+ CFTimeInterval now =
+ [layer convertTime:CACurrentMediaTime() fromLayer:nil] - parentBeginTime;
+ reversedAnimation.fillMode = animationToReverse.fillMode;
+ reversedAnimation.removedOnCompletion =
+ animationToReverse.removedOnCompletion;
+ reversedAnimation.timingFunction = animationToReverse.timingFunction;
+
+ // Extract the previous reversal if it exists.
+ ReversedAnimation* previousReversedAnimation = nil;
+ Protocol* reversedAnimationProtocol = @protocol(ReversedAnimationProtocol);
+ if ([animationToReverse conformsToProtocol:reversedAnimationProtocol]) {
+ previousReversedAnimation =
+ static_cast<ReversedAnimation*>(animationToReverse);
+ animationToReverse = previousReversedAnimation.originalAnimation;
+ }
+ reversedAnimation.originalAnimation = animationToReverse;
+ reversedAnimation.animationDirection =
+ previousReversedAnimation
+ ? AnimationDirectionOpposite(
+ previousReversedAnimation.animationDirection)
+ : ANIMATION_DIRECTION_REVERSE;
+
+ CAAnimation* previousAnimation = previousReversedAnimation
+ ? previousReversedAnimation
+ : animationToReverse;
+ BOOL isReversed =
+ reversedAnimation.animationDirection == ANIMATION_DIRECTION_REVERSE;
+ if (now < previousAnimation.beginTime) {
+ // Reversal occurs before previous animation begins.
+ reversedAnimation.beginTime = 2.0 * now - previousAnimation.beginTime -
+ reversedAnimation.originalAnimation.duration;
+ reversedAnimation.duration = reversedAnimation.originalAnimation.duration;
+ reversedAnimation.animationTimeOffset =
+ isReversed ? 0 : reversedAnimation.originalAnimation.duration;
+ } else if (now < previousAnimation.beginTime + previousAnimation.duration) {
+ // Reversal occurs while the previous animation is occurring.
+ reversedAnimation.beginTime = 0;
+ CFTimeInterval timeDelta = now - previousAnimation.beginTime;
+ reversedAnimation.animationTimeOffset =
+ previousReversedAnimation.animationTimeOffset +
+ (isReversed ? 1.0 : -1.0) * timeDelta;
+ reversedAnimation.duration =
+ isReversed ? reversedAnimation.animationTimeOffset
+ : reversedAnimation.originalAnimation.duration -
+ reversedAnimation.animationTimeOffset;
+ } else {
+ // Reversal occurs after the previous animation has ended.
+ if (!parentBeginTime) {
+ // If the parent's begin time is zero, the animation is using absolute
+ // time as its beginTime.
+ reversedAnimation.beginTime =
+ 2.0 * now - previousAnimation.beginTime - previousAnimation.duration;
+ } else {
+ CFTimeInterval previousEndTime =
+ previousAnimation.beginTime + previousAnimation.duration;
+ if (now > parent.duration) {
+ // The animation's parent has already ended, so use the difference
+ // between the parent's ending and the previous animation's end.
+ reversedAnimation.beginTime = parent.duration - previousEndTime;
+ } else {
+ // The parent hasn't ended, so use the difference between the current
+ // time and the previous animation's end.
+ reversedAnimation.beginTime = now - previousEndTime;
+ }
+ }
+ reversedAnimation.duration = reversedAnimation.originalAnimation.duration;
+ reversedAnimation.animationTimeOffset =
+ isReversed ? reversedAnimation.originalAnimation.duration : 0;
+ }
+}
+
+void ReverseAnimationsForKeyForLayers(NSString* key, NSArray* layers) {
+ for (CALayer* layer in layers) {
+ CAAnimation* reversedAnimation =
+ CAAnimationMakeReverse([layer animationForKey:key], layer);
+ [layer removeAnimationForKey:key];
+ [layer addAnimation:reversedAnimation forKey:key];
+ }
+}
« 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