动效解析工厂:Mask 动画
转载自:http://www.cocoachina.com/ios/20160214/15250.html
前言:很多动效都是多种动画的组合,有时候你可能只是需要其中某个动画,但面对庞杂的代码库或是教程,你可能比较困惑,本系列将复杂动效中不常见的动画效果拆解出来便于学习,授人以鱼不如授人以渔。

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

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

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 套路实现上面的效果慢放后是下面这样的,不知道原作者有没有对这两种效果进行过对比,老实说,我觉得原作者的手法实现的效果更好:

NewBubbleTransition
上面方法的核心代码:
//利用 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")
来点不一样的形状,当然从效果上来讲这个形状放在这里不好看。该例子仅作示范。

quincunx.gif
生成梅花形状曲线的代码:
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()

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

Angles in the default coordinate system
在绘制上面的梅花形状的曲线时,注意起始和终点的曲线保持整体的绘制方向一致,不然动画会很奇怪。
总结
差点搞了个大乌龙,不过见识到 BubbleTransition 的实现手段,现在就有两种手段来实现这类效果:
尺寸更大的圆角矩形视图充当背景
UIBezierPath + CAShapeLayer + maskLayer
我对 BubbleTransition 的手法真是佩服得五体投地,就这么简单地搞定了,UIBezierPath + CAShapeLayer + maskLayer 的很多场景都可以使用这个手法替代,代价也不高。当然面对更复杂的曲线视图,还是用后者比较省心。
UIBezierPath + CAShapeLayer + maskLayer 的组合拳非常适合实现一些不规则的视图,像曲线菜单或任务栏,波纹视图,灌水视图等等,发挥下你的想象力吧。
动效解析工厂:Mask 动画的更多相关文章
- iOS开发之 Lottie -- 炫酷的动效
动效在软件开发中非常常见,炫酷的动画能提升应用的B格,然而由设计师的设计转化成程序猿GG的代码是个非常"痛苦"的过程.对于复杂动画,可能要花费很多时间去研究和实现.Lottie 的 ...
- Web动效研究与实践
随着CSS3和HTML5的发展,越来越多狂拽炫酷叼炸天的动效在网页设计上遍地开花,根据最新的浏览器市场份额报告,IE6的份额已经降到了5.21%,这简直是一个喜大普奔的消息,做动效可以完全不care低 ...
- 新版MATERIAL DESIGN 官方动效指南(一)
Google 刚发布了新版Material Design 官方动效指南,全文包括三个部分:为什么说动效很重要?如何制作优秀的Material Design动效及转场动画,动效的意义.新鲜热辣收好不谢! ...
- iOS动画进阶 - 实现炫酷的上拉刷新动效
移动端訪问不佳,请訪问我的个人博客 近期撸了一个上拉刷新的小轮子.仅仅要遵循一个协议就能自己定义自己动效的上拉刷新和载入,我自己也写了几个动效进去,以下是一个比較好的动效的实现过程 先上效果图和git ...
- 从浏览器渲染层面解析css3动效优化原理
引言 在h5开发中,我们经常会需要实现一些动效来让页面视觉效果更好,谈及动效便不可避免地会想到动效性能优化这个话题: 减少页面DOM操作,可以使用CSS实现的动效不多出一行js代码 使用绝对定位脱离让 ...
- QQ音乐的动效歌词是如何实践的?
本文由云+社区发表 作者:QQ音乐技术团队 一. 背景 1. 现状 歌词浏览已经成为音乐app的标配,展示和动画效果也基本上大同小异,主要是单行的逐字染色的卡拉OK效果和多行的滚动效果.当然,我们也不 ...
- 拒绝枯燥,有意思的 Loading 页面动效设计
互联网时代,网络“提速”日益频繁,人们打开Web或软件的速度越来越快,一般页面缓冲和加载地过程也是几不可查.然而,在某些情况下,例如软件急需加载大量页面,首页急需加载大量内容,用户下载文件过大,甚至是 ...
- 巧用 -webkit-box-reflect 倒影实现各类动效
在很久之前的一篇文章,有讲到 -webkit-box-reflect 这个属性 -- 从倒影说起,谈谈 CSS 继承 inherit -webkit-box-reflect 是一个非常有意思的属性,它 ...
- iOS开发Facebook POP动效库使用教程
如果说Origami这款动效原型工具是Facebook Paper的幕后功臣,那么POP便是Origami的地基.感谢Facebook开源了POP动效库,让人人都能制作出华丽的动效.我们只需5步,便能 ...
随机推荐
- IIS短文件漏洞修复
近期网站系统被扫描出漏洞:IIS短文件/文件夹漏洞 漏洞级别:中危漏洞 漏洞地址:全网站 漏洞描述:IIS短文件名泄露漏洞,IIS上实现上存在文件枚举漏洞,攻击者可利用此漏洞枚举获取服务器根目录中的文 ...
- Android webView 正确的用法
Android webView 正确的用法 引言: 我在网络找了几个例子,基本上都有问题,<Android疯狂讲义>13.4中的源代码也有问题.终于在官网找到正确的用法.点我. 基本用法: ...
- 简单的ALloctor模板
template <typename T>class Alloctor{private: typedef T* address; T *memory,*t; size_t total_si ...
- js模板引擎实现原理
将html模板放入script标签中 // "> // ]]> 使用javascript开始解析模板 // )[^\t]*)'/g,"$1\r"). repl ...
- Anniversary Party
Time limit: 0.5 second Memory limit: 8 MB Background The president of the Ural State University is g ...
- JMeter入门(1):JMeter总体介绍及组件介绍
一.JMeter概述 JMeter就是一个测试工具,相比于LoadRunner等测试工具,此工具免费,且比较好用,但是前提当然是安装Java环境: JMeter可以做 (1)压力测试及性能测试: (2 ...
- [每日一题] OCP1z0-047 :2013-08-12 view视图的描述哪些是正确的?
正确答案是: CE 这是OCP教材中的: 1.简单视图与复杂视图的定义: 2.复杂视图通常不能被DML: .WITH CHECKOP TIONT选项 A不正确.简单视图可以被更新. hr@OCM> ...
- UVa 872 - Ordering 输出全拓扑排序
本题要求输出所有拓扑排序的序列. 还好本题的数据量不是非常大.限制在26个大写英文字母,故此能够使用递归法输出. 这个递归输出所有解在Leetcode非常多这种题目的,不小心的话,还是非常难调试的. ...
- 兔子--R.java丢失原因及解决的方法
R.jar丢失原因: a:eclipse指向的adk路径有中文,或者是workspace路径有中文 b:xml文件里有错误或者引用的资源不存在 c:xml或者drawable下资源文件不能够有大写字母 ...
- C# 动态创建出来的窗体间的通讯 delegate1
附件 http://files.cnblogs.com/xe2011/CSharp_WindowsForms_delegate01.rar 需要每个窗体是独立存在,禁止相与引用窗体 这样干净并且可以反 ...