iOS动画系列之七:实现类似Twitter的启动动画

Twitter的”fave”动画

2015/05/12 · HTML5 ·
Twitter,
动画

本文由 伯乐在线 –
刘健超-J.c
翻译,胡屹
校稿。未经许可,禁止转载!
英文出处:cssanimation.rocks。欢迎加入翻译组。

当我第一次打开 twitter
的时候,就被它的启动动画惊艳到了。然而分析一下这个动画其实也不难实现,于是赶紧做一个出来看看。

来来来,今天咱们通过实现一个类似Twitter的启动动画来看看CAKeyFrame
Animation和CAAnimation Group怎么玩。

十大超越原作的动画——三爷比漫画美多了

2016-9-17 00:08| 发布者: 团长| 查看: 683 | 评论: 0 |来自: 网络

有很多漫画被改编成动画后,观众都会对原作和动画进行对比。有日媒评出了十大超越原作漫画的动画,其中就包括了毫无节操的《银魂》以及原作漫画潦草到吓人的《进击的巨人》。

十大超越原作漫画的动画

第1位:《鲁邦三世》(作者:Monkey Punch)

第2位:《银魂》

第3位:《蜡笔小新》

第4位:《轻音少女!》(作者:kakifly)

第5位:《海螺小姐》

第6位:《樱桃小丸子》

第7位:《进击的巨人》

第8位:《星空清理者》

第9位:《妙手小厨师》

第10位:《根性青蛙》

点击下载漫社堂APP,看更多人气动漫作品

【漫社堂动漫资讯部于9月17日整理报道,转载请注明出处!】

喜欢 偷看 热烈 这是神马 睡去

作者写了一个给 iPhone X 去掉刘海的 APP,而且其他 iPhone 也可以玩,有兴趣的话去 App Store 看看。点击前往。

澳门威尼斯人赌场 1

现在进入正题。没错,就是告诉你 0 行代码拥有 Facebook 和 Twitter
这个动画。

澳门威尼斯人赌场 2JPPopPresentAnimation.gif

要实现这个功能分两步走:第一步,先实现这个动画。第二步,0
行代码集成到项目。也就是,你不用改原有项目的任何代码,只要把写好的动画代码拖进项目,以后所有的
Modal 就拥有这个动画了。

- presentViewController:(UIViewController *)viewControllerToPresent animated: flag completion:(void (^ __nullable)completion

好,准备好了吗?现在开始。

Twitter的“fave” 动画

最近 Twitter
通过引入一段新的动画重新设计了“fave”按钮(也叫“fav”)。这段动画并不依赖
CSS transition,而是由一系列图片组成的。下面展示如何用 CSS 的
animation-timing-function 属性中的 steps 时序函数(timing
function)重新制作这段动画。

澳门威尼斯人赌场 3

所以今天咱们的重点到了第七章,CAKeyFrame Animation和CAAnimation
Group。最后的那个启动动画完全是为了实践一下看看CAKeyFrame
Animation和CAAnimation Group怎么使用。

第一步:先实现这个动画

  • 首先我们来分析一下这个动画,这个动画就是在 Modal澳门威尼斯人赌场
    的时候拿到当前控制器的 View,然后在 Modal 的过程中将这个 View
    的尺寸缩小。然后在 Dismiss 的时候拿到这个
    View,将它尺寸恢复成原有的大小。就这么多了。

  • 怎么在 modal 动画中拿到当前的控制器的
    View?这个很简单,只要你设置动画样式为自定义,并且成为弹出动画的代理,就像这样:

self.modalTransitionStyle = UIModalPresentationCustom;viewControllerToPresent.transitioningDelegate = self;然后在代理方法中,会询问转场动画的提供者,此时,你再去新建一个JPTransitionManager作为转场动画的提供者。// 询问转场动画的提供者- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
  • 在这个转场动画的提供者里,我们继承
    NSObject,然后遵守UIViewControllerAnimatedTransitioning
    协议,然后实现协议中这两个方法:

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;- animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

第一个方法是询问动画的时长的。第二个方法中我们可以拿到准备做 Modal
动画的上下文
transitionContext,在上下文中我们就可以通过:UITransitionContextFromViewControllerKey
UITransitionContextToViewControllerKey 两个 key
拿到当前的控制器和即将弹出的控制器。

  • 控制器都拿到了,我们就可以拿到控制器对应的 View 进行动画了。

运动产生的错觉

这段动画的效果类似于观看古老的西洋镜,该装置呈现的是一系列连续的围绕着圆筒的插画。在下面的示例中,我们不使用圆筒,而是在某个元素内部呈现一系列图片。

  • 布置一个与 LaunchScreen 相同的界面
  • 让视图中间的 Logo 先缩小后放大直至盖满整个屏幕
  • Logo 在放大过程中逐渐变透明
  • Initial View Controller 的内容稍微放大后恢复原状

有读者私下说更新速度太慢了。在码云上看了一下下载的统计,发现其实下载的童鞋并不是特别多。如果只是看看思路,或者复习一下这些基础知识,确实是很快。但是如果对于这些内容不是特别熟悉,建议还是敲一边代码,看看自己能碰到什么坑。

第二步:0 行代码集成到项目

为什么要强调 0 行代码集成到项目?

  • 刚开始写代码的时候,项目老大并没有说要这个动画,你项目都快结束了,他说这个动画挺好,我们也做一个吧(程序员转过身:XXX脏话)。这个时候你已经写了几百个
    Modal 动画的代码了,你不可能一个一个去找去改吧?

  • 几百行你改完了,你以为终于完事了,这件事就过去了,结果产品经理一个月以后又跑过来说,这个功能我们还是砍掉吧。(程序员大声:XXXXXX很脏的话)。

所以,这个时候低耦合易集成就显得特别重要。

为了处理这样的需求,所以采用 hook 方案,结合 runtime 来解决。hook
就是钩子的意思。

  • 我们要用这个钩子把系统所有的 Modal
    动画的实现都钩出来,然后再把我们自己写的 pop 动画也给钩出来。
  • 然后再运用动态方法交换,将这系统的 Modal 动画和自己的 pop
    动画的实现相互替换。
  • 所以以后,我们再调用系统的 Modal
    动画的时候,实际上会去方法实现里找我们自己的 pop
    动画去执行。这也就是为什么可以 0 行代码集成这个功能的原因。
  • 也有可能我们在某些情况下不需要pop动画,比如说照片浏览器。这种情况很少,所以你只需要写很少的代码,去调用我们自己的
    pop 动画。当调用我们自己的 pop
    动画的时候,我们的钩子会去找系统的 Modal 动画去执行。

思路就到这里了,具体的实现细节,还需要您自己去Github下载 demo 。

下面这个链接是我所有文章的一个集合目录。这些文章凡是涉及实现的,每篇文章中都有
Github 地址,Github 上都有源码。

我的文章集合索引

示例

把鼠标悬停在星星上就可以看到动画效果(请到原文查看动画效果——译者注)。

在本示例中,我们将从制作一系列能组成动画的图片开始。在这里,我们使用来自
Twitter 的“fave”图标动画的部分图片集:

澳门威尼斯人赌场 4

为了能让这些帧动起来,我们需要把它们放置在一排上。在这个文件中,这些帧已经排列在一排上了,这意味着我们可以通过设置背景位置(background-position)属性使背景从第一帧过渡到最后一帧。

澳门威尼斯人赌场 5

拆分完之后就好办咯,一步步来实现吧~

俺写一篇分享文章大约要4~6个小时,大体是三部分:想到合适的例子,敲代码写注释,写文章。通常都会看自己当前的情况,决定是先写swift版还是OC版,然后不动脑子的翻译成另外一版调整一下BUG。这样也是为了训练自己,前段时间发现自己有时候会不自觉的把两种语言混在一起,这个习惯特别不好,所以想用这种方式自己纠正一下。到最后更新写文章的时候反而更轻松了,因为不用动脑。哈哈~

你还可以关注我自己维护的简书专题 iOS开发心得。这个专题的文章都是实打实的干货。如果你有问题,除了在文章最后留言,还可以在微博 @盼盼_HKbuy上给我留言,以及访问我的 Github。

你这一赞助,我写的就更来劲了!

微信赞助扫码

澳门威尼斯人赌场 6

支付宝赞助扫码

澳门威尼斯人赌场 7

Steps() 时序函数

大多数的时序函数,例如 ease(缓冲)和
cubic-bezier(三次贝塞尔),都能让元素从初始状态平滑地过渡到最终状态。steps
时序函数与此不同,它并不是平滑地过渡,而是将过渡过程分割为一定数量的步骤,并且在这些步骤之间快速地移动。

澳门威尼斯人赌场 8

我们先建立如下的 HTML 代码:

XHTML

<section class=”fave”></section>

1
<section class="fave"></section>

由于后面需要让 logo 变透明,我们选择用 mask 来实现。

Come
on~下面这张图纯粹是为了简书当作封面使用的。也不知道为什么,以前简书还能自动把GIF的第一桢当作封面,现在不好使了。

背景图片

接下来, 我们可以添加一些样式并设置背景图片位置:

澳门威尼斯人赌场 9

CSS

.fave { width: 70px; height: 50px; background:
url(images/twitter_fave.png) no-repeat; background-position: 0 0; }

1
2
3
4
5
6
.fave {
  width: 70px;
  height: 50px;
  background: url(images/twitter_fave.png) no-repeat;
  background-position: 0 0;
}

加了悬停状态后,一旦鼠标悬停在该元素上,背景就会从我们指定的位置移动到这一系列图片中最后一张的位置上(为了兼容浏览器,注意要添加相应的浏览器内核前缀——译者注)。

CSS

.fave:hover{ animation: fave 1s steps(55); } @keyframes fave{ 0%{
background-position:0 0; } 100%{ background-position:-3519px 0; } }

1
2
3
4
5
6
7
8
9
10
11
.fave:hover{
  animation: fave 1s steps(55);
}
@keyframes fave{
  0%{
    background-position:0 0;
  }
  100%{
    background-position:-3519px 0;
  }
}

请注意第1个规则 animation。在本例中,我们利用 steps
时序函数,让background-position 属性经历了一个持续时间为1秒的过渡。在
steps 部分的“55”这个值,代表了这段动画是由55帧组成的。

当我们将鼠标悬停在这个元素上时,所看到的效果是其背景图片通过55个相同的步骤经历了一次过渡。

另外这个案例,也可以用 transition 实现:

CSS

.fave:hover { background-position: -3519px 0; transition: background 1s
steps(55); }

1
2
3
4
.fave:hover {
  background-position: -3519px 0;
  transition: background 1s steps(55);
}
let logoLayer = CALayer()logoLayer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)logoLayer.position = view.centerlogoLayer.contents = UIImage(named: "logo")?.cgImageview.layer.mask = logoLayer

CAKeyFrame Animation和CAAnimation Group.png

为什么不使用gif?

虽然也可以使用 gif 动画,但在这个案例中并不是很合适。gif
文件的大小通常较大并且帧速率也难以控制。而使用这个方式,我们就可以用 CSS
对这个动画进行停止、倒回以及各种各样的调整。

澳门威尼斯人赌场 10

下面展示一下写完之后的成果:

“steps()”的其它用法

背景动画精灵(background sprites)仅仅只是 steps
时序函数的用法之一。除此之外该函数还适用于制作任何需要一系列离散步骤的动画。例如,你可以用该函数制作一个摆钟。

好了,小鸟出来了。

ani.gif

备忘小条

如果你喜欢这篇文章,你可以将它分享在Twitter,或者保存下面的备忘小条,以便参考。

澳门威尼斯人赌场 11

打赏支持我翻译更多好文章,谢谢!

打赏译者

但一开始这个 logo 并不是透明的,于是先在其上盖一层白色的
view,并改一下背景颜色吧。

源代码可以在这里下载,里面有OC和Swift两版。https://git.oschina.net/atypical/CAKeyFrame-\_Group\_Animation.git

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

澳门威尼斯人赌场 12
澳门威尼斯人赌场 13

赞 收藏
评论

let shelterView = UIView(frame: view.frame)shelterView.backgroundColor = .whiteview.addSubview(shelterView)window!.backgroundColor = UIColor(red: 29 / 255.0, green: 161 / 255.0, blue: 242 / 255.0, alpha: 1)

iOS动画系列之CAKeyFrame Animation和CAAnimation
Group(OC和Swift两版)

关于作者:刘健超-J.c

澳门威尼斯人赌场 14

前端,在路上…
个人主页 ·
我的文章 ·
19 ·
    

澳门威尼斯人赌场 15

澳门威尼斯人赌场 16

1. CAKeyframeAnimation

CAKeyframeAnimation是CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。

创建步骤:

  1. 创建关键帧动画对象
  2. 设置属性
  3. 添加到要作用的layer上
  • 如果使用rect椭圆的方式,动画会不连贯,停顿一下。原因是因为矩形的周长比椭圆的长,动画路径按照椭圆执行完之后,需要等待一下最大周长走完。
    这些都是因为计算模式导致的。

棒,第一步完成。

1.1 创建一个抖动的小方块

我们用一个简单的demo来感受一下CAKeyframeAnimation,来做一个会抖动的小方块。

抖动的小方块.gif

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
//设置一些列的关键帧动画
    animation.values = @[@(-M_PI_4 / 5),@(M_PI_4 / 5),@(-M_PI_4 / 5)];
    animation.repeatCount = CGFLOAT_MAX;
    [self.view.layer addAnimation:animation forKey:@"rotation"];

这里我们用
CAKeyframeAnimation,让这个缩放动作一气呵成。设置好开始时间、持续时间和各个关键帧,最后让它保持动画最后的状态。

1.2 创建一个沿椭圆路径运动的小飞机

我们创建一个UIBezierPath,让小飞机沿着这个路径运动。

//创建动画对象
    CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//设置属性:创建beziper路径,并把路径作为运动轨迹
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 500)];
    keyFrameAnimation.path = bezierPath.CGPath;
//设置动画时间
    keyFrameAnimation.duration = 2;
//设置动画循环播放的次数
    keyFrameAnimation.repeatCount = CGFLOAT_MAX;
//设置动画的计算模式
    keyFrameAnimation.calculationMode = kCAAnimationPaced;

//将动画添加到layer上
    [self.planeView.layer addAnimation:keyFrameAnimation forKey:nil];
let logoAnimation = CAKeyframeAnimation(keyPath: "bounds")logoAnimation.beginTime = CACurrentMediaTime() + 1logoAnimation.duration = 1logoAnimation.keyTimes = [0, 0.4, 1]logoAnimation.values = [NSValue(cgRect: CGRect(x: 0, y: 0, width: 100, height: 100)), NSValue(cgRect: CGRect(x: 0, y: 0, width: 85, height: 85)), NSValue(cgRect: CGRect(x: 0, y: 0, width: 4500, height: 4500))]logoAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)]logoAnimation.isRemovedOnCompletion = falselogoAnimation.fillMode = kCAFillModeForwardslogoLayer.add(logoAnimation, forKey: "zoomAnimation")

1.3 动画叠加

刚才添加了一个沿路径运动的飞机,我们同时还可以给飞机再把抖动的那个动画也添加上去。前几篇提到后面那个forKey,可能还有童鞋不知道干啥用。现在看到了木有?一个layer里面好几个动画,如何找到对应的动画吶?现在通过这个key就能找到了。

    //        为小飞机同时添加抖动的动画和椭圆路径旋转的动画
    [self.planeImageView.layer addAnimation:[self shakeAni] forKey:nil];
    [self.planeImageView.layer addAnimation:[self ovalAni] forKey:nil];

澳门威尼斯人赌场 17

2. CAAnimationGroup

单一的动画在实际中往往是不能满足需求的,这时就需要用到动画组。

  • 是CAAnimation的子类
  • 可以保存一组动画对象,将CAAnimationGroup对象加入图层后,组中所有动画对象可以同时并发运行.

我们试着做一个包行旋转、缩放、按一定弧度路径组合在一起的动画。效果如下:

arcAni.gif

- (CAAnimationGroup *)groupAni{
    //        实例化一个组动画对象
        CAAnimationGroup *groupAni = [[CAAnimationGroup alloc] init];

    //        创建旋转的动画
        CABasicAnimation *basicRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    basicRotation.toValue = @(M_PI * 2);

    //        创建缩放的动画
        CABasicAnimation *basicScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    basicScale.toValue = @(0.2);

    //        创建按照路径移动的动画
        CAKeyframeAnimation *keyFrameAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:150 startAngle:0 endAngle:M_PI * 2 clockwise:YES];

    keyFrameAni.path = arcPath.CGPath;
    keyFrameAni.calculationMode = kCAAnimationPaced;
    keyFrameAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    //        将旋转、缩放、移动的动画添加到组动画中
    groupAni.animations = @[basicRotation,basicScale,keyFrameAni];
    //        组动画重复次数
        groupAni.repeatCount = CGFLOAT_MAX;
    //        组动画时长
        groupAni.duration = 2;

    return groupAni;
}

又好了。

3. 实现类似Twitter的启动动画

这个算好渐变透明的时间就好。

3.1实现思路

1,在View上设置一个东西能够遮挡住背景图;
2,把遮罩变成五角星;
3,让遮罩慢慢变大,中间可见区域越来越大。

yes!思路就是这样的。那怎么遮住背景图片呢?

UIView.animate(withDuration: 0.3, delay: 1.4, options: .curveLinear, animations: { shelterView.alpha = 0}) {  in shelterView.removeFromSuperview() view.layer.mask = nil}

3.2 CALayer的遮罩属性

CALayer本身有一个属性,叫mask。我们来看一下官方解释:

@property(nullable, strong) CALayer *mask;

When true an implicit mask matching the layer bounds is applied to
the layer (including the effects of the `cornerRadius' property). If
both `mask' and `masksToBounds' are non-nil the two masks are
multiplied to get the actual mask values. Defaults to NO.
Animatable.

它类似于一个子图层,相对于父图层(即拥有该属性的图层)布局,但是它却不是一个普通的子图层。不同于其他能够在父图层中绘制出图像的子图层,mask图层定义了父图层的部分可见区域。

mask图层的Color属性是无关紧要的,真正重要的是图层的轮廓。也就是说mask图层实心的部分会被保留下来,其他的则会被抛弃。如果mask图层比父图层要小,只有在mask图层里面的内容才是它关心的,除此以外的一切都会被隐藏起来。

Paste_Image.png

澳门威尼斯人赌场 18

3.3 实现类似Twitter的启动动画

好了准备工作都做完了,我们就开始写这个动画了。这个动画其实就是一个简单的CAKeyframeAnimation。设置了三个关键帧动画的大小,以及这三个关键帧的运动节奏。

然后,就好啦~然后,就好啦~然后,就好啦~然后,就好啦~

哪尼?!!!就这样?!!对啊,就这样。

 - (CAKeyframeAnimation *)maskAni{
 //        放大缩小视图,keypath使用bounds
 CAKeyframeAnimation *maskAni = [CAKeyframeAnimation animationWithKeyPath:@"bounds"];
 //        动画时间
 maskAni.duration = 30.75;
 //        动画延迟0.5秒播放
 maskAni.beginTime = CACurrentMediaTime() + 0.5;


 //        设置关键帧动画的数值
 CGRect startRect = self.maskLayer.frame;

 CGRect tempRect = CGRectMake(0, 0, 100, 100);

 CGRect finalRect = CGRectMake(0, 0, 2000, 2000);
 maskAni.values = @[[NSValue valueWithCGRect:startRect],[NSValue valueWithCGRect:tempRect],[NSValue valueWithCGRect:finalRect]];


 //        设置关键帧动画的运动节奏
 maskAni.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];


 //        动画播放结束后是否移除动画
 maskAni.removedOnCompletion = NO;

 //        动画填充模式:kCAFillModeForwards:当动画结束后,layer会一直保持着动画最后的状态
 maskAni.fillMode = kCAFillModeForwards;

 return maskAni;


 }

留一个小问题

道理跟第二步相同,就不说了~

我在OC和Swift里面对不同的View使用了mask。一个是给背景图片的UIImageView设置了mask,另一个是直接给Controller的View设置了mask。设置这两个有神马区别咩?

好,下篇其实有一个重头,就是CAShapeLayer。因为在工作中碰到的大部分动画都是通过UIView的动画block实现,其他都基本上都是需要用到CAShapeLayer。我们下次玩点好玩的吧~
如果还有兴趣,可以看看本系列的其他文章哈。

———————–华丽分割线,iOS动画系列全集链接————————————————-
第一篇:iOS动画系列之一:通过实战学习CALayer和透视的原理。做一个带时分秒指针的时钟动画(上)
第二篇:iOS动画系列之二:通过实战学习CALayer和透视的原理。做一个带时分秒指针的时钟动画。包含了OC和Swift两种源代码(下)
第三篇:iOS动画系列之三:Core Animation。介绍了Core
Animation的常用属性和方法。
第四篇:CABasic
Animation。iOS动画系列之四:基础动画之平移篇
第五篇:CABasic
Animation。iOS动画系列之五:基础动画之缩放篇&旋转篇
第六篇:iOS动画系列之六:利用CABasic
Animation完成带动画特效的登录界面
第七篇:iOS动画系列之七:实现类似Twitter的启动动画
第八篇:iOS动画系列之八:使用CAShapeLayer绘画动态流量图
第九篇:iOS动画系列之九:实现点赞的动画及播放起伏指示器
第十篇:实战系列:绘制过山车场景

let mainViewAnimation = CAKeyframeAnimation(keyPath: "transform")mainViewAnimation.beginTime = CACurrentMediaTime() + 1.1mainViewAnimation.duration = 0.6mainViewAnimation.keyTimes = [0, 0.5, 1]mainViewAnimation.values = [NSValue(caTransform3D: CATransform3DIdentity), NSValue(caTransform3D: CATransform3DScale(CATransform3DIdentity, 1.1, 1.1, 1)), NSValue(caTransform3D: CATransform3DIdentity)]view.layer.add(mainViewAnimation, forKey: "transformAnimation")view.layer.transform = CATransform3DIdentity

澳门威尼斯人赌场 19

Done.

Github: TwitterLauncher

发表评论

电子邮件地址不会被公开。 必填项已用*标注