动画效果解析工厂:Mask 动画

简介:

前言:很多动效都是多种动画的组合,有时候你可能只是需要其中某个动画,但面对庞杂的代码库或是教程,你可能比较困惑,本系列将复杂动效中不常见的动画效果拆解出来便于学习,授人以鱼不如授人以渔。

37334-81771f6702ae0923.gif

第一讲是来自 BubbleTransition 中最夺人眼球的形变动画。这个效果在 StarWars.iOS转场动效有两次应用,非常地炫酷;Raywenderlich.com 也出过一个教程来实现这种效果。

15.gif

BubbleTransition

一般而言,这种效果会使用 UIBezierPath + CAShapeLayer + maskLayer 搞定,但是我去看了看代码,上面的效果其实是下面这样实现的。

37334-f26ca3b5dbd9d756.gif

BubbleTransition 慢放

Are you kidding me? Are you kidding me? 不知道我为何说两遍的请去欣赏《拯救呆萌》三部曲最终篇《火星救援》。这个效果的开发者真是太有才了,仅仅通过组合视图+缩放这么简单的方法就实现了这个效果,天才。

在上面提到的另外两种效果则是使用提到的 UIBezierPath + CAShapeLayer + maskLayer 套路,关于 UIBezierPath + CAShapeLayer,简书上有一篇写得还不错,就是标题太炫酷,这篇文章示范了使用 UIBezierPath + CAShapeLayer 实现不规则视图,也可以使用上面的简单组合手法轻松实现,但如果面对更加复杂的图形,还是得靠 UIBezierPath + CAShapeLayer。

也许你听说过贝塞尔曲线,但在 iOS 里,UIBezierPath 不仅仅用于生成一条曲线,常规的矩形、圆形、圆角矩形以及椭圆都不在话下,是个普适性的图形对象。而 CAShapeLayer 则是 CALayer 的子类,正如类名描述一样,通过其path属性搭配 UIBezierPath 可以实现多种用普通手段难以实现的外形,以及一些线条动画(可以去看看上面提到的标题很炫酷的文章)。

而 maskLayer,你可能听说过遮罩之类的概念,很像你玩游戏探索地图时的效果,这里实际上指的是CALayer类的mask属性,也是个CALayer对象,UIView类有个maskView的属性,作用相似。其实 BubbleTransition 里作者的实现手法本身就是对 mask 这一概念的应用,真的是太天才了。

使用 UIBezierPath + CAShapeLayer + maskLayer 套路实现上面的效果慢放后是下面这样的,不知道原作者有没有对这两种效果进行过对比,老实说,我觉得原作者的手法实现的效果更好:

26.gif

NewBubbleTransition

上面方法的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//利用 UIBezierPath 生成一大一小两条椭形路径,ovalInRect 意思是生成紧贴矩形内部的椭圆,bubble 是 BubbleTransition 中的添加的背景视图,
let circleMaskPathInitial = UIBezierPath(ovalInRect: bubble.frame)
let finalRect = CGRectInset(bubble.frame, -1000, -1000)
let circleMaskPathFinal = UIBezierPath(ovalInRect: finalRect)
//用 CAShapeLayer 作为 mask
let maskLayer = CAShapeLayer()
maskLayer.path = circleMaskPathFinal.CGPath
presentedControllerView.layer.mask = maskLayer
//对 CAShapeLayer 的 path 属性进行动画
let maskLayerAnimation = CABasicAnimation(keyPath:  "path" )
maskLayerAnimation.fromValue = circleMaskPathInitial.CGPath
maskLayerAnimation.toValue = circleMaskPathFinal.CGPath
maskLayerAnimation.duration = self.transitionDuration(transitionContext)
maskLayer.addAnimation(maskLayerAnimation, forKey:  "path" )

来点不一样的形状,当然从效果上来讲这个形状放在这里不好看。该例子仅作示范。

27.gif

quincunx.gif

生成梅花形状曲线的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let width = presentedControllerView.frame.width
let height = presentedControllerView.frame.height
 
let circleMaskPathInitial = UIBezierPath()
circleMaskPathInitial.moveToPoint(CGPoint(x: 0, y: height))
circleMaskPathInitial.addArcWithCenter(CGPoint(x: width / 4, y: height), radius: width / 4, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 3 / 2), clockwise:  true )
circleMaskPathInitial.addArcWithCenter(CGPoint(x: width / 2, y: height - width / 4), radius: width / 4, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 2), clockwise:  true )
circleMaskPathInitial.addArcWithCenter(CGPoint(x: width * 3 / 4, y: height), radius: width / 4, startAngle: CGFloat(M_PI * 3 / 2), endAngle:CGFloat(M_PI * 2), clockwise:  true )
circleMaskPathInitial.moveToPoint(CGPoint(x: 0, y: height))
circleMaskPathInitial.closePath()
 
let circleMaskPathFinal = UIBezierPath()
circleMaskPathFinal.moveToPoint(CGPoint(x: -width, y: height))
circleMaskPathFinal.addArcWithCenter(CGPoint(x: 0, y: height), radius: height, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 3 / 2), clockwise:  true )
circleMaskPathFinal.addArcWithCenter(CGPoint(x: width / 2, y: 0), radius: width / 2, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 2), clockwise:  true )
circleMaskPathFinal.addArcWithCenter(CGPoint(x: width, y: height), radius: height, startAngle: CGFloat(M_PI * 3 / 2), endAngle: CGFloat(M_PI * 2), clockwise:  true )
circleMaskPathFinal.moveToPoint(CGPoint(x: -width, y: height))
circleMaskPathFinal.closePath()

37334-1141ecdd38e818cb.png

示意图

类方法bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:用于添加圆弧,clockwise参数指示绘制的方向,true时沿顺时针方向绘制,false时沿逆时针方向绘制。下图是该方法文档中的坐标示意图:

37334-0ffaf490f31275ac.png

Angles in the default coordinate system

在绘制上面的梅花形状的曲线时,注意起始和终点的曲线保持整体的绘制方向一致,不然动画会很奇怪。

总结

差点搞了个大乌龙,不过见识到 BubbleTransition 的实现手段,现在就有两种手段来实现这类效果:

  • 尺寸更大的圆角矩形视图充当背景

  • UIBezierPath + CAShapeLayer + maskLayer

我对 BubbleTransition 的手法真是佩服得五体投地,就这么简单地搞定了,UIBezierPath + CAShapeLayer + maskLayer 的很多场景都可以使用这个手法替代,代价也不高。当然面对更复杂的曲线视图,还是用后者比较省心。

UIBezierPath + CAShapeLayer + maskLayer 的组合拳非常适合实现一些不规则的视图,像曲线菜单或任务栏,波纹视图,灌水视图等等,发挥下你的想象力吧。


目录
相关文章
|
12天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
19 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
4月前
|
设计模式
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道
|
4月前
|
设计模式 XML JSON
二十三种设计模式全面解析-工厂模式:创造对象的魔法工厂
二十三种设计模式全面解析-工厂模式:创造对象的魔法工厂
|
前端开发 JavaScript
复刻解析一个流光溢彩炫到掉渣的 CSS 动画按钮
最近在看 next.js 官网是被引流到 conf 页面,发现上面有一个炫酷的按钮,按钮的边框色彩不断变动给人感觉是光在随着按钮旋转一般,感觉挺酷的,复刻一下讲解下原理。
|
存储 网络协议 安全
数据原来是这么传输的(结合动画解析)
从1990年,世界上第一个互联网Web网页诞生,到现在2021年,已经过去了30多个年头。
135 0
数据原来是这么传输的(结合动画解析)
|
算法 搜索推荐 Java
十大经典排序算法动画解析和 Java 代码实现【详细全代码】
排序算法可以分为内部排序和外部排序。内部排序是数据记录在内存中进行排序。而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。 常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
80 0
十大经典排序算法动画解析和 Java 代码实现【详细全代码】
|
Java uml Spring
Spring Cloud Gateway源码解析实战 - 路由工厂
Spring Cloud Gateway源码解析实战 - 路由工厂
197 0
Spring Cloud Gateway源码解析实战 - 路由工厂
|
算法 搜索推荐 Java
十大经典排序算法动画与解析,看我就够了!(配代码完全版) | 算法必看系列三十八
排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为 内部排序 和 外部排序 。内部排序是数据记录在内存中进行排序。而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
十大经典排序算法动画与解析,看我就够了!(配代码完全版) | 算法必看系列三十八
|
机器学习/深度学习 人工智能 自然语言处理
注意力的动画解析(以机器翻译为例)
近十年以来,直到神经机器翻译系统的诞生之前,统计机器翻译一直在机器翻译领域占据着主要地位。
328 0

推荐镜像

更多