| // 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/animation_util.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| |
| #include "ios/chrome/browser/ui/reversed_animation.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| CAAnimation* FrameAnimationMake(CALayer* layer, |
| CGRect beginFrame, |
| CGRect endFrame) { |
| CGRect beginBounds = {CGPointZero, beginFrame.size}; |
| CGRect endBounds = {CGPointZero, endFrame.size}; |
| CABasicAnimation* boundsAnimation = |
| [CABasicAnimation animationWithKeyPath:@"bounds"]; |
| boundsAnimation.fromValue = [NSValue valueWithCGRect:beginBounds]; |
| boundsAnimation.toValue = [NSValue valueWithCGRect:endBounds]; |
| boundsAnimation.removedOnCompletion = NO; |
| boundsAnimation.fillMode = kCAFillModeBoth; |
| CGPoint beginPosition = CGPointMake( |
| beginFrame.origin.x + layer.anchorPoint.x * beginBounds.size.width, |
| beginFrame.origin.y + layer.anchorPoint.y * beginBounds.size.height); |
| CGPoint endPosition = CGPointMake( |
| endFrame.origin.x + layer.anchorPoint.x * endBounds.size.width, |
| endFrame.origin.y + layer.anchorPoint.y * endBounds.size.height); |
| CABasicAnimation* positionAnimation = |
| [CABasicAnimation animationWithKeyPath:@"position"]; |
| positionAnimation.fromValue = [NSValue valueWithCGPoint:beginPosition]; |
| positionAnimation.toValue = [NSValue valueWithCGPoint:endPosition]; |
| positionAnimation.removedOnCompletion = NO; |
| positionAnimation.fillMode = kCAFillModeBoth; |
| return AnimationGroupMake(@[ boundsAnimation, positionAnimation ]); |
| } |
| |
| CAAnimation* OpacityAnimationMake(CGFloat beginOpacity, CGFloat endOpacity) { |
| CABasicAnimation* opacityAnimation = |
| [CABasicAnimation animationWithKeyPath:@"opacity"]; |
| opacityAnimation.fromValue = @(beginOpacity); |
| opacityAnimation.toValue = @(endOpacity); |
| opacityAnimation.fillMode = kCAFillModeBoth; |
| opacityAnimation.removedOnCompletion = NO; |
| return opacityAnimation; |
| } |
| |
| CAAnimation* AnimationGroupMake(NSArray* animations) { |
| CAAnimationGroup* animationGroup = [CAAnimationGroup animation]; |
| animationGroup.animations = animations; |
| CFTimeInterval duration = 0.0; |
| for (CAAnimation* animation in animations) |
| duration = std::max(duration, animation.beginTime + animation.duration); |
| animationGroup.duration = duration; |
| animationGroup.fillMode = kCAFillModeBoth; |
| animationGroup.removedOnCompletion = NO; |
| return animationGroup; |
| } |
| |
| CAAnimation* DelayedAnimationMake(CAAnimation* animation, |
| CFTimeInterval delay) { |
| CAAnimation* delayedAnimation = [animation copy]; |
| if (delayedAnimation) { |
| delayedAnimation.beginTime = delay; |
| delayedAnimation = AnimationGroupMake(@[ delayedAnimation ]); |
| } |
| return delayedAnimation; |
| } |
| |
| CABasicAnimation* FindAnimationForKeyPath(NSString* keyPath, |
| CAAnimation* animation) { |
| __block CABasicAnimation* animationForKeyPath = nil; |
| if ([animation isKindOfClass:[CABasicAnimation class]]) { |
| CABasicAnimation* basicAnimation = |
| static_cast<CABasicAnimation*>(animation); |
| if ([basicAnimation.keyPath isEqualToString:keyPath]) |
| animationForKeyPath = basicAnimation; |
| } else if ([animation isKindOfClass:[CAAnimationGroup class]]) { |
| CAAnimationGroup* animationGroup = |
| static_cast<CAAnimationGroup*>(animation); |
| [animationGroup.animations |
| enumerateObjectsUsingBlock:^(CAAnimation* subAnimation, NSUInteger idx, |
| BOOL* stop) { |
| animationForKeyPath = FindAnimationForKeyPath(keyPath, subAnimation); |
| *stop = animationForKeyPath != nil; |
| }]; |
| } |
| return animationForKeyPath; |
| } |
| |
| void RemoveAnimationForKeyFromLayers(NSString* key, NSArray* layers) { |
| for (CALayer* layer in layers) |
| [layer removeAnimationForKey:key]; |
| } |