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

Unified Diff: ios/chrome/browser/ui/stack_view/page_animation_util.mm

Issue 2587023002: Upstream Chrome on iOS source code [8/11]. (Closed)
Patch Set: Created 4 years 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
Index: ios/chrome/browser/ui/stack_view/page_animation_util.mm
diff --git a/ios/chrome/browser/ui/stack_view/page_animation_util.mm b/ios/chrome/browser/ui/stack_view/page_animation_util.mm
new file mode 100644
index 0000000000000000000000000000000000000000..3e66bd4c4b76ecaffb79eb2d6e049eef8334fe7d
--- /dev/null
+++ b/ios/chrome/browser/ui/stack_view/page_animation_util.mm
@@ -0,0 +1,474 @@
+// Copyright 2012 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.
+
+#import "ios/chrome/browser/ui/stack_view/page_animation_util.h"
+
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+#import "base/mac/scoped_nsobject.h"
+#import "ios/chrome/browser/ui/animation_util.h"
+#include "ios/chrome/browser/ui/rtl_geometry.h"
+#import "ios/chrome/browser/ui/stack_view/card_view.h"
+#import "ios/chrome/common/material_timing.h"
+
+using ios::material::TimingFunction;
+
+namespace {
+
+const NSTimeInterval kAnimationDuration = 0.25;
+const NSTimeInterval kAnimationHesitation = 0.2;
+
+// Constants used for rotating/translating in in transition-in animations and
+// rotating/translating out in transition-out animations.
+const CGFloat kDefaultRotation = 0.2094; // 12 degrees.
+// The amount by which the card should be translated along the axis on which
+// its short side is oriented (horizontal in portrait, vertical in landscape).
+const CGFloat kDefaultShortSideAxisTranslation = 240;
+// The amount by which the card should be translated along the axis on which
+// its long side is oriented (vertical in portrait, horizontal in landscape).
+const CGFloat kDefaultLongSideAxisTranslation = 10;
+
+// Transitioning in on landscape has a special-case animation.
+const CGFloat kLandscapeAnimateInRotation = 0.9423; // 54 degrees.
+const CGFloat kLandscapeAnimateInShortSideAxisTranslation = -180;
+const CGFloat kLandscapeAnimateInLongSideAxisTranslation = 140;
+
+NSString* const kViewAnimateInKey = @"ViewAnimateIn";
+NSString* const kPaperAnimateInKey = @"PaperAnimateIn";
+
+// When animating out, a card shrinks slightly.
+const CGFloat kAnimateOutScale = 0.7;
+const CGFloat kAnimateOutAnchorX = 0.9;
+const CGFloat kAnimateOutAnchorY = 0;
+}
+
+@interface PaperView : UIView
+
+@end
+
+@implementation PaperView
+
+- (id)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ const UIEdgeInsets kShadowStretchInsets = {28.0, 28.0, 28.0, 28.0};
+ const UIEdgeInsets kShadowLayoutOutset = {-10.0, -11.0, -12.0, -11.0};
+ CGRect shadowFrame = UIEdgeInsetsInsetRect(frame, kShadowLayoutOutset);
+ base::scoped_nsobject<UIImageView> frameShadowImageView(
+ [[UIImageView alloc] initWithFrame:shadowFrame]);
+ [frameShadowImageView
+ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
+ UIViewAutoresizingFlexibleHeight)];
+ [self addSubview:frameShadowImageView];
+
+ UIImage* image = [UIImage imageNamed:@"popup_background"];
+ image = [image resizableImageWithCapInsets:kShadowStretchInsets];
+ [frameShadowImageView setImage:image];
+ }
+ return self;
+}
+
+@end
+
+namespace ios_internal {
+
+namespace page_animation_util {
+
+const CGFloat kCardMargin = 14.0;
+
+void SetNewTabAnimationStartPositionForView(UIView* view, BOOL isPortrait) {
+ CGAffineTransform transform = CGAffineTransformMakeTranslation(
+ (isPortrait ? kDefaultShortSideAxisTranslation
+ : kLandscapeAnimateInLongSideAxisTranslation),
+ (isPortrait ? kDefaultLongSideAxisTranslation
+ : kLandscapeAnimateInShortSideAxisTranslation));
+ transform = CGAffineTransformRotate(
+ transform, (isPortrait ? kDefaultRotation : kLandscapeAnimateInRotation));
+ view.transform = transform;
+}
+
+void AnimateInPaperWithAnimationAndCompletion(UIView* view,
+ CGFloat paperOffset,
+ CGFloat contentOffset,
+ CGPoint origin,
+ BOOL isOffTheRecord,
+ void (^extraAnimation)(void),
+ void (^completion)(void)) {
+ CGRect endFrame = view.frame;
+ UIView* parent = [view superview];
+ NSInteger index = [[parent subviews] indexOfObject:view];
+
+ // Create paper background.
+ CGRect paperFrame = CGRectOffset(endFrame, 0, paperOffset);
+ paperFrame.size.height -= paperOffset;
+ base::scoped_nsobject<PaperView> paper(
+ [[PaperView alloc] initWithFrame:paperFrame]);
+ [parent insertSubview:paper belowSubview:view];
+ [paper addSubview:view];
+ [paper setBackgroundColor:isOffTheRecord
+ ? [UIColor colorWithWhite:34 / 255 alpha:1]
+ : [UIColor whiteColor]];
+
+ [CATransaction begin];
+ [CATransaction setCompletionBlock:^{
+
+ // Put view back where it belongs, with its original frame.
+ [parent insertSubview:view atIndex:index];
+ [paper removeFromSuperview];
+ [[view layer] removeAnimationForKey:kViewAnimateInKey];
+ view.frame = endFrame;
+ if (completion)
+ completion();
+ }];
+ [CATransaction setAnimationDuration:ios::material::kDuration5];
+ CAMediaTimingFunction* transformCurve2 = ios::material::TransformCurve2();
+
+ // // Animate paper to full size.
+ CABasicAnimation* scaleAnimation =
+ [CABasicAnimation animationWithKeyPath:@"transform"];
+ scaleAnimation.fromValue =
+ [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.03, 0.03, 1)];
+ scaleAnimation.timingFunction = transformCurve2;
+ scaleAnimation.duration = ios::material::kDuration1;
+
+ CABasicAnimation* positionAnimation =
+ [CABasicAnimation animationWithKeyPath:@"position"];
+ positionAnimation.fromValue = [NSValue valueWithCGPoint:origin];
+ positionAnimation.timingFunction = transformCurve2;
+ positionAnimation.duration = ios::material::kDuration1;
+
+ CAAnimation* fadeAnimation = OpacityAnimationMake(0, 1);
+ fadeAnimation.timingFunction = transformCurve2;
+ fadeAnimation.duration = ios::material::kDuration1;
+ [[paper layer]
+ addAnimation:AnimationGroupMake(
+ @[ scaleAnimation, positionAnimation, fadeAnimation ])
+ forKey:kPaperAnimateInKey];
+
+ // Animate content from -10px to full size, as a child of the paper parent.
+ // At the half-way point, the child will be offset / 2 vertically higher than
+ // the paper parent, but be sure to account for paperOriginYOffset, which
+ // allows for pages to draw above |parent| (as the new tab page does).
+ CGFloat offset = -10;
+ CGFloat width = endFrame.size.width;
+ CGFloat height = endFrame.size.height - contentOffset;
+ CGRect startFrame = CGRectMake(0, offset, width, height);
+ CGRect middleFrame =
+ CGRectMake(0, offset / 2 - paperOffset + contentOffset, width, height);
+ CGRect childEndFrame =
+ CGRectMake(0, -paperOffset + contentOffset, width, height);
+
+ CAAnimation* frameAnimation =
+ FrameAnimationMake([view layer], startFrame, middleFrame);
+ frameAnimation.timingFunction = transformCurve2;
+ frameAnimation.duration = ios::material::kDuration1;
+ frameAnimation.fillMode = kCAFillModeBackwards;
+
+ CAMediaTimingFunction* fadeInCurve =
+ TimingFunction(ios::material::CurveEaseOut);
+ CAAnimation* frameAnimation2 =
+ FrameAnimationMake([view layer], middleFrame, childEndFrame);
+ frameAnimation2.timingFunction = fadeInCurve;
+ frameAnimation2.duration = ios::material::kDuration1;
+ frameAnimation2.beginTime = ios::material::kDuration1;
+ frameAnimation2.fillMode = kCAFillModeForwards;
+
+ fadeAnimation = OpacityAnimationMake(0, 1);
+ fadeAnimation.timingFunction = fadeInCurve;
+ fadeAnimation.duration = ios::material::kDuration5;
+ [[view layer]
+ addAnimation:AnimationGroupMake(
+ @[ frameAnimation, frameAnimation2, fadeAnimation ])
+ forKey:kViewAnimateInKey];
+
+ [CATransaction commit];
+}
+
+void AnimateInCardWithAnimationAndCompletion(UIView* view,
+ void (^extraAnimation)(void),
+ void (^completion)(void)) {
+ SetNewTabAnimationStartPositionForView(view, true);
+ [UIView animateWithDuration:kAnimationDuration
+ delay:0
+ options:UIViewAnimationCurveEaseOut
+ animations:^{
+ view.transform = CGAffineTransformIdentity;
+ if (extraAnimation)
+ extraAnimation();
+ }
+ completion:^(BOOL finished) {
+ if (completion)
+ completion();
+ }];
+}
+
+void AnimateNewBackgroundPageWithCompletion(CardView* currentPageCard,
+ CGRect displayFrame,
+ BOOL isPortrait,
+ void (^completion)(void)) {
+ // Create paper background.
+ base::scoped_nsobject<PaperView> paper(
+ [[PaperView alloc] initWithFrame:CGRectZero]);
+ UIView* parent = [currentPageCard superview];
+ [parent insertSubview:paper aboveSubview:currentPageCard];
+ CGRect pageBounds = currentPageCard.bounds;
+ [paper setCenter:CGPointMake(CGRectGetMidX(pageBounds),
+ CGRectGetMidY(pageBounds))];
+ [paper setBackgroundColor:[UIColor whiteColor]];
+ [paper setAlpha:0.0];
+
+ CGSize pageSize = currentPageCard.bounds.size;
+ CGRect paperFrame =
+ CGRectMake((displayFrame.size.width - pageSize.width) / 2,
+ CGRectGetMidY(pageBounds), pageSize.width, pageSize.height);
+
+ // The animation of the current page during the new background card animation
+ // has three parts:
+ // 1. It shrinks the current tab image into an inset card view.
+ // 2. It hesitates for a fraction of a second.
+ // 3. It expands back out, transforming again into the current tab.
+ // |currentPageCard| gives the card at the correct size for step 2, as it
+ // appears in the slight hesitation. Here, the code creates the transform
+ // that will start by displaying the card at full size; the animation then
+ // moves the card into its original state, and back out to full screen size.
+ CGSize displaySize = displayFrame.size;
+ CGFloat fullScreenScale =
+ (displaySize.width + kCardImageInsets.left + kCardImageInsets.right +
+ kCardFrameImageSnapshotOverlap) /
+ currentPageCard.frame.size.width;
+ // Align the bottom of |currentPageCard|'s snapshot with the bottom of the
+ // screen, so that snapshots of any height are correctly aligned with the
+ // tab's content.
+ currentPageCard.center = CGPointMake(
+ displaySize.width / 2.0, displaySize.height -
+ (currentPageCard.image.size.height / 2.0) -
+ kCardImageInsets.top / 2);
+ CGAffineTransform fullScreenTransform =
+ CGAffineTransformMakeScale(fullScreenScale, fullScreenScale);
+ currentPageCard.transform = fullScreenTransform;
+ [currentPageCard setTabOpacity:0.0];
+
+ [CATransaction begin];
+ [CATransaction
+ setAnimationTimingFunction:TimingFunction(ios::material::CurveEaseIn)];
+ [UIView animateWithDuration:kAnimationDuration
+ delay:0
+ options:UIViewAnimationCurveLinear
+ animations:^{
+ [currentPageCard setTabOpacity:1.0];
+ currentPageCard.transform = CGAffineTransformIdentity;
+ [paper setFrame:paperFrame];
+ [paper setAlpha:1.0];
+ }
+ completion:^(BOOL finished) {
+ // Zoom out the top tab, slide away the paper view.
+ [UIView animateWithDuration:kAnimationDuration
+ delay:kAnimationHesitation
+ options:UIViewAnimationCurveLinear
+ animations:^{
+ [currentPageCard setTabOpacity:0.0];
+ currentPageCard.transform = fullScreenTransform;
+ [paper setFrame:CGRectOffset(paperFrame, 0,
+ CGRectGetMaxY(paperFrame))];
+ [paper setAlpha:0.0];
+
+ }
+ completion:^(BOOL finished) {
+ [paper removeFromSuperview];
+ if (completion)
+ completion();
+ }];
+ }];
+ [CATransaction commit];
+}
+
+void AnimateNewBackgroundTabWithCompletion(CardView* currentPageCard,
+ CardView* newCard,
+ CGRect displayFrame,
+ BOOL isPortrait,
+ void (^completion)(void)) {
+ // The animation of the current page during the new background card animation
+ // has three parts:
+ // 1. It shrinks the current tab image into an inset card view.
+ // 2. It hesitates for a fraction of a second.
+ // 3. It expands back out, transforming again into the current tab.
+ // |currentPageCard| gives the card at the correct size for step 2, as it
+ // appears in the slight hesitation. Here, the code creates the transform
+ // that will start by displaying the card at full size; the animation then
+ // moves the card into its original state, and back out to full screen size.
+ CGSize displaySize = displayFrame.size;
+ CGFloat fullScreenScale =
+ (displaySize.width + kCardImageInsets.left + kCardImageInsets.right) /
+ currentPageCard.frame.size.width;
+ // Align the bottom of |currentPageCard|'s snapshot with the bottom of the
+ // screen, so that snapshots of any height are correctly aligned with the
+ // tab's content.
+ currentPageCard.center = CGPointMake(
+ displaySize.width / 2.0,
+ displaySize.height - (currentPageCard.image.size.height / 2.0));
+ CGAffineTransform fullScreenTransform =
+ CGAffineTransformMakeScale(fullScreenScale, fullScreenScale);
+ currentPageCard.transform = fullScreenTransform;
+ [currentPageCard setTabOpacity:0.0];
+
+ // The new background card animation has three parts:
+ // 1. It moves from offscreen onto the screen (in a "rotating" motion).
+ // 2. It hesitates for a fraction of a second, halfway on the screen.
+ // 3. It moves from the screen offscreen (in a "sliding" motion).
+ // In the setup code below, we position the card on the screen as it will
+ // appear in step 2; in the animation code, we then move it to and from this
+ // original onscreen position.
+ //
+ // In portrait mode, the card in step 2 appears to be halfway off the bottom
+ // edge of the screen; in landscape mode, it appears to be halfway off the
+ // right edge. The x and y offsets below set up this screen position.
+ CGFloat yOffset = -displayFrame.origin.y + kCardMargin +
+ (isPortrait ? displaySize.height / 2.0 : 0);
+ CGFloat xOffset = isPortrait ? kCardMargin : displaySize.width / 2.0;
+ CGRect newCardFrame = newCard.frame;
+ newCardFrame.origin.x += xOffset;
+ newCardFrame.origin.y += yOffset;
+ newCard.frame = newCardFrame;
+
+ // For step 1, we apply a transform to the card that moves it offscreen and
+ // rotates it away in preparation for the "rotate in" animation that starts
+ // any new tab appearance.
+ SetNewTabAnimationStartPositionForView(newCard, isPortrait);
+
+ // For step 3, we create a transform which will slide the card offscreen along
+ // its longer axis to end the animation.
+ CGAffineTransform slideAwayTransform =
+ isPortrait
+ ? CGAffineTransformMakeTranslation(0, newCard.frame.size.height)
+ : CGAffineTransformMakeTranslation(newCard.frame.size.width, 0);
+
+ [UIView animateWithDuration:kAnimationDuration
+ delay:0
+ options:UIViewAnimationCurveEaseOut
+ animations:^{
+ [currentPageCard setTabOpacity:1.0];
+ currentPageCard.transform = CGAffineTransformIdentity;
+ newCard.transform = CGAffineTransformIdentity;
+ }
+ completion:^(BOOL finished) {
+ // Zoom out the top tab, slide away the new card.
+ [UIView animateWithDuration:kAnimationDuration
+ delay:kAnimationHesitation
+ options:UIViewAnimationCurveEaseOut
+ animations:^{
+ [currentPageCard setTabOpacity:0.0];
+ currentPageCard.transform = fullScreenTransform;
+ newCard.transform = slideAwayTransform;
+ }
+ completion:^(BOOL finished) {
+ if (completion)
+ completion();
+ }];
+ }];
+}
+
+void UpdateLayorAnchorWithTransform(CALayer* layer,
+ CGPoint newAnchor,
+ CGAffineTransform transform) {
+ CGSize size = layer.bounds.size;
+ CGPoint oldAnchor = layer.anchorPoint;
+ CGPoint newCenter =
+ CGPointMake(size.width * newAnchor.x, size.height * newAnchor.y);
+ CGPoint oldCenter =
+ CGPointMake(size.width * oldAnchor.x, size.height * oldAnchor.y);
+
+ newCenter = CGPointApplyAffineTransform(newCenter, transform);
+ oldCenter = CGPointApplyAffineTransform(oldCenter, transform);
+
+ CGPoint position = layer.position;
+ position.x = position.x - oldCenter.x + newCenter.x;
+ position.y = position.y - oldCenter.y + newCenter.y;
+ layer.position = position;
+
+ layer.anchorPoint = newAnchor;
+}
+
+void AnimateOutWithCompletion(UIView* view,
+ NSTimeInterval delay,
+ BOOL clockwise,
+ BOOL isPortrait,
+ void (^completion)(void)) {
+ // The close animation spec calls for the anchor point to be the upper right.
+ CGPoint newAnchorPoint = CGPointMake(kAnimateOutAnchorX, kAnimateOutAnchorY);
+ CALayer* layer = [view layer];
+ UpdateLayorAnchorWithTransform(layer, newAnchorPoint, view.transform);
+
+ [CATransaction begin];
+ if (completion)
+ [CATransaction setCompletionBlock:completion];
+
+ [CATransaction setAnimationDuration:ios::material::kDuration6];
+ CAMediaTimingFunction* timing = TimingFunction(ios::material::CurveEaseIn);
+ [CATransaction setAnimationTimingFunction:timing];
+
+ CABasicAnimation* scaleAnimation =
+ [CABasicAnimation animationWithKeyPath:@"transform"];
+ CATransform3D transform = CATransform3DScale(
+ layer.transform, kAnimateOutScale, kAnimateOutScale, 1);
+ [scaleAnimation setToValue:[NSValue valueWithCATransform3D:transform]];
+
+ CABasicAnimation* fadeAnimation =
+ [CABasicAnimation animationWithKeyPath:@"opacity"];
+ [fadeAnimation setFromValue:[NSNumber numberWithFloat:[layer opacity]]];
+ [fadeAnimation setToValue:@0];
+
+ [layer addAnimation:AnimationGroupMake(@[ scaleAnimation, fadeAnimation ])
+ forKey:@"animateOut"];
+ [CATransaction commit];
+}
+
+CGAffineTransform AnimateOutTransform(CGFloat fraction,
+ BOOL clockwise,
+ BOOL isPortrait) {
+ CGFloat horizontalTranslation = isPortrait ? kDefaultShortSideAxisTranslation
+ : kDefaultLongSideAxisTranslation;
+ CGFloat verticalTranslation = isPortrait ? kDefaultLongSideAxisTranslation
+ : kDefaultShortSideAxisTranslation;
+ CGFloat rotationAmount = kDefaultRotation;
+
+ if (!isPortrait && UseRTLLayout()) {
+ rotationAmount *= -1;
+ horizontalTranslation *= -1;
+ }
+
+ horizontalTranslation *= fraction;
+ verticalTranslation *= fraction;
+ rotationAmount *= fraction;
+ if (!clockwise)
+ rotationAmount *= -1;
+
+ // In portrait, rotating counterclockwise pushes the animation to the left.
+ if (isPortrait && !clockwise) {
+ horizontalTranslation *= -1;
+ }
+
+ // In landscape, rotating clockwise pushes the animation up.
+ if (!isPortrait && clockwise) {
+ verticalTranslation *= -1;
+ }
+
+ // Scale the card between full-scale and the final desired scale based on
+ // |fraction|.
+ CGFloat differenceInScale = 1.0 - kAnimateOutScale;
+ CGFloat scaleAmount = 1.0 - (differenceInScale * fraction);
+ CGAffineTransform transform = CGAffineTransformMakeTranslation(
+ horizontalTranslation, verticalTranslation);
+ transform = CGAffineTransformRotate(transform, rotationAmount);
+ transform = CGAffineTransformScale(transform, scaleAmount, scaleAmount);
+ return transform;
+}
+
+CGFloat AnimateOutTransformBreadth() {
+ return kDefaultShortSideAxisTranslation;
+}
+
+} // namespace page_animation_util
+
+} // namespace ios_internal
« no previous file with comments | « ios/chrome/browser/ui/stack_view/page_animation_util.h ('k') | ios/chrome/browser/ui/stack_view/stack_card.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698