iOS复杂动画之抽丝剥茧(Objective-C & Swift)

ntzw6497 8年前
   <h2>一、前言</h2>    <p>随着开发者的增多和时间的累积,<code>AppStore</code>已经有非常多的应用了,每年都有很多新的<code>APP</code>产生。但是我们手机上留存的应用有限,所以如何吸引用户,成为产品设计的一项重要内容。其中炫酷的动画效果是重要内容之一,我们会发现很多好的应用上面都有许多很炫的效果。可能一提到炫酷的动画,很多人都很头疼,因为动画并不是那么好做,实现一个好的动画需要时间、耐心和好的思路。下面我们就以一个有趣的动画(如下图)为例,抽丝剥茧,看看到底是怎么实现的!</p>    <p style="text-align:center"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/98823f8198bd91ade9664d843e59ac7c.gif"></p>    <h2>二、分析</h2>    <p>上面图中的动画第一眼看起来的确是有点复杂,但是我们来一步步分析,就会发现其实并不是那么难。仔细看一下就会发现,大致步骤如下:</p>    <ul>     <li>1、先出来一个圆</li>     <li>2、圆形在水平和竖直方向上被挤压,呈椭圆形状的一个过程,最后恢复成圆形</li>     <li>3、圆形的左下角、右下角和顶部分别按顺序凸出一小部分</li>     <li>4、圆和凸出部分形成的图形旋转一圈后变成三角形</li>     <li>5、三角形的左边先后出来两条宽线,将三角形围在一个矩形中</li>     <li>6、矩形由底部向上被波浪状填满</li>     <li>7、被填满的矩形放大至全屏,弹出<code>Welcome</code></li>    </ul>    <p>动画大致就分为上面几个步骤,拆分后我们一步步来实现其中的效果(下面所示步骤中以<code>Swift</code>代码为例,<code>demo</code>中分别有<code>Objective-C</code>和<code>Swift</code>的实现)。</p>    <h2>三、实现圆形以及椭圆的渐变</h2>    <p>首先,我们创建了一个新工程后,然后新建了一个名<code>AnimationView</code>的类继承<code>UIView</code>,这个是用来显示动画效果的一个<code>view</code>。然后先添加<code>CircleLayer</code>(圆形<code>layer</code>),随后实现由小变大的效果。</p>    <pre>  <code class="language-objectivec">    class AnimationView: UIView {                let circleLayer = CircleLayer()                override init(frame: CGRect) {                      super.init(frame: frame)                      backgroundColor = UIColor.clearColor()                      addCircleLayer()                  }                required init?(coder aDecoder: NSCoder) {                      super.init(coder: aDecoder)                  }                /**                add circle layer                */              func addCircleLayer() {                      self.layer.addSublayer(circleLayer)                      circleLayer.expand()                  }    }</code></pre>    <p>其中<code>expand()</code>这个方法如下</p>    <pre>  <code class="language-objectivec">    /**       expand animation function       */      func expand() {          let expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")          expandAnimation.fromValue = circleSmallPath.CGPath          expandAnimation.toValue = circleBigPath.CGPath          expandAnimation.duration = KAnimationDuration          expandAnimation.fillMode = kCAFillModeForwards          expandAnimation.removedOnCompletion = false          self.addAnimation(expandAnimation, forKey: nil)      }</code></pre>    <p>运行效果如下</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/704bb7c24069715b59e6ea0782c4b931.gif"></p>    <p>第一步做好了,接下来就是呈椭圆形状的变化了,仔细分析就比如一个弹性小球,竖直方向捏一下,水平方向捏一下这样的效果。这其实就是一个组合动画,如下</p>    <pre>  <code class="language-objectivec">    /**       wobbl group animation       */      func wobbleAnimate() {          // 1、animation begin from bigPath to verticalPath          let animation1: CABasicAnimation = CABasicAnimation(keyPath: "path")          animation1.fromValue = circleBigPath.CGPath          animation1.toValue = circleVerticalSquishPath.CGPath          animation1.beginTime = KAnimationBeginTime          animation1.duration = KAnimationDuration            // 2、animation vertical to horizontal          let  animation2: CABasicAnimation = CABasicAnimation(keyPath: "path")          animation2.fromValue = circleVerticalSquishPath.CGPath          animation2.toValue = circleHorizontalSquishPath.CGPath          animation2.beginTime = animation1.beginTime + animation1.duration          animation2.duration = KAnimationDuration            // 3、animation horizontal to vertical          let  animation3: CABasicAnimation = CABasicAnimation(keyPath: "path")          animation3.fromValue = circleHorizontalSquishPath.CGPath          animation3.toValue = circleVerticalSquishPath.CGPath          animation3.beginTime = animation2.beginTime + animation2.duration          animation3.duration = KAnimationDuration            // 4、animation vertical to bigPath          let  animation4: CABasicAnimation = CABasicAnimation(keyPath: "path")          animation4.fromValue = circleVerticalSquishPath.CGPath          animation4.toValue = circleBigPath.CGPath          animation4.beginTime = animation3.beginTime + animation3.duration          animation4.duration = KAnimationDuration            // 5、group animation          let animationGroup: CAAnimationGroup = CAAnimationGroup()          animationGroup.animations = [animation1, animation2, animation3, animation4]          animationGroup.duration = 4 * KAnimationDuration          animationGroup.repeatCount = 2          addAnimation(animationGroup, forKey: nil)      }</code></pre>    <p>上面代码中实现了从 <code>圆 → 椭圆(x方向长轴)→ 椭圆(y方向长轴)→ 圆</code>这一系列的变化,最后组合成一个动画。这一步实现后效果如下</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/71e5f8080901230a69f40dd83036a8fe.gif"></p>    <h2>四、实现圆形边缘的凸出部分</h2>    <p>关于这个凸出部分,乍一看可能感觉会比较难实现,看起来挺复杂的。其实实现的原理很简单,仔细分析我们会发现这三个凸出部分连起来刚好是一个三角形,那么第一步我们就在之前的基础上先加一个三角形的<code>layer</code>,如下</p>    <pre>  <code class="language-objectivec">import UIKit    class TriangleLayer: CAShapeLayer {        let paddingSpace: CGFloat = 30.0        override init() {           super.init()          fillColor = UIColor.colorWithHexString("#009ad6").CGColor          strokeColor = UIColor.colorWithHexString("#009ad6").CGColor          lineWidth = 7.0          path = smallTrianglePath.CGPath      }        required init?(coder aDecoder: NSCoder) {          fatalError("init(coder:) has not been implemented")      }        var smallTrianglePath: UIBezierPath {          let smallPath = UIBezierPath()          smallPath.moveToPoint(CGPointMake(5.0 + paddingSpace, 95.0))          smallPath.addLineToPoint(CGPointMake(50.0, 12.5 + paddingSpace))          smallPath.addLineToPoint(CGPointMake(95.0 - paddingSpace, 95.0))          smallPath.closePath()          return smallPath      }  }</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/3e3a24c71046c298e068d31ddb9ef923.png"></p>    <p>然后设置圆角</p>    <pre>  <code class="language-objectivec">        lineCap = kCALineCapRound          lineJoin = kCALineJoinRound</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/83847254715170fcc56ee86b6f1cb399.png"></p>    <p>下面就是来做凸出部分了,原理其实很简单,就是将现在这个三角形保持中心不变,左边向左延伸即可</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/413e246197882370edb2119c2fc90c2f.gif"></p>    <p>然后同理,保持中心不变分别按顺序向右和向上拉伸</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/b9e2f8098f38e7b681f79bdc310d33e1.gif"></p>    <p>具体过程是这样的</p>    <pre>  <code class="language-objectivec">    /**       triangle animate function       */      func triangleAnimate() {           // left          let triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: "path")          triangleAnimationLeft.fromValue = smallTrianglePath.CGPath          triangleAnimationLeft.toValue = leftTrianglePath.CGPath          triangleAnimationLeft.beginTime = 0.0          triangleAnimationLeft.duration = 0.3           // right          let triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: "path")          triangleAnimationRight.fromValue = leftTrianglePath.CGPath          triangleAnimationRight.toValue = rightTrianglePath.CGPath          triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration          triangleAnimationRight.duration = 0.25           // top          let triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: "path")          triangleAnimationTop.fromValue = rightTrianglePath.CGPath          triangleAnimationTop.toValue = topTrianglePath.CGPath          triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.duration          triangleAnimationTop.duration = 0.20           // group          let triangleAnimationGroup: CAAnimationGroup = CAAnimationGroup()          triangleAnimationGroup.animations = [triangleAnimationLeft, triangleAnimationRight, triangleAnimationTop]          triangleAnimationGroup.duration = triangleAnimationTop.beginTime + triangleAnimationTop.duration          triangleAnimationGroup.fillMode = kCAFillModeForwards          triangleAnimationGroup.removedOnCompletion = false          addAnimation(triangleAnimationGroup, forKey: nil)      }</code></pre>    <p>我们接下来把三角形的颜色改一下</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/e149c01b5b587c9024ce1fff70a203dc.gif"></p>    <p>这里颜色相同了我们就可以看到了这个凸出的这个效果,调到正常速率(为了演示,把动画速率调慢了) ,联合之前所有的动作,到现在为止,效果是这样的</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/c793f1bd59532594ac00c2f6b58100a3.gif"></p>    <p><br> 到现在为止,看上去还不错,差不多已经完成一半了,继续下一步!</p>    <h2>五、实现旋转和矩形</h2>    <p>旋转来说很简单了,大家估计都做过旋转动画,这里就是把前面形成的图形旋转一下(当然要注意设置锚点<code>anchorPoint</code>)</p>    <pre>  <code class="language-objectivec">    /**       self transform z       */      func transformRotationZ() {          self.layer.anchorPoint = CGPointMake(0.5, 0.65)          let rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")          rotationAnimation.toValue = CGFloat(M_PI * 2)          rotationAnimation.duration = 0.45          rotationAnimation.removedOnCompletion = true          layer.addAnimation(rotationAnimation, forKey: nil)      }</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/f09d0f2f8b3cd986268a818753c1e6de.gif"></p>    <p>旋转之后原图形被切成了一个三角形,思路就是把原来的大圆,按着这个大三角形的内切圆剪切一下即可</p>    <pre>  <code class="language-objectivec">    /**       contract animation function       */      func contract() {          let contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")          contractAnimation.fromValue = circleBigPath.CGPath          contractAnimation.toValue = circleSmallPath.CGPath          contractAnimation.duration = KAnimationDuration          contractAnimation.fillMode = kCAFillModeForwards          contractAnimation.removedOnCompletion = false          addAnimation(contractAnimation, forKey: nil)      }</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/2988db70be3e4d183e14fcf3fb285fd5.gif"></p>    <p>接下来就是画矩形,新建一个<code>RectangleLayer</code>,划线</p>    <pre>  <code class="language-objectivec">    /**       line stroke color change with custom color         - parameter color: custom color       */      func strokeChangeWithColor(color: UIColor) {          strokeColor = color.CGColor          let strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")          strokeAnimation.fromValue = 0.0          strokeAnimation.toValue = 1.0          strokeAnimation.duration = 0.4          addAnimation(strokeAnimation, forKey: nil)      }</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/e77a2548187c1ab1ee2cec4450b52904.gif"></p>    <p><br> 最后面就是经典的水波纹动画了,不多说,直接上代码</p>    <pre>  <code class="language-objectivec">![WavetAnimation.gif](http://upload-images.jianshu.io/upload_images/571495-856dc8f307d16f60.gif?imageMogr2/auto-orient/strip)        func animate() {           /// 1          let waveAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path")          waveAnimationPre.fromValue = wavePathPre.CGPath          waveAnimationPre.toValue = wavePathStarting.CGPath          waveAnimationPre.beginTime = 0.0          waveAnimationPre.duration = KAnimationDuration           /// 2          let waveAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path")          waveAnimationLow.fromValue = wavePathStarting.CGPath          waveAnimationLow.toValue = wavePathLow.CGPath          waveAnimationLow.beginTime = waveAnimationPre.beginTime + waveAnimationPre.duration          waveAnimationLow.duration = KAnimationDuration           /// 3          let waveAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path")          waveAnimationMid.fromValue = wavePathLow.CGPath          waveAnimationMid.toValue = wavePathMid.CGPath          waveAnimationMid.beginTime = waveAnimationLow.beginTime + waveAnimationLow.duration          waveAnimationMid.duration = KAnimationDuration           /// 4          let waveAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path")          waveAnimationHigh.fromValue = wavePathMid.CGPath          waveAnimationHigh.toValue = wavePathHigh.CGPath          waveAnimationHigh.beginTime = waveAnimationMid.beginTime + waveAnimationMid.duration          waveAnimationHigh.duration = KAnimationDuration           /// 5          let waveAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path")          waveAnimationComplete.fromValue = wavePathHigh.CGPath          waveAnimationComplete.toValue = wavePathComplete.CGPath          waveAnimationComplete.beginTime = waveAnimationHigh.beginTime + waveAnimationHigh.duration          waveAnimationComplete.duration = KAnimationDuration           /// group animation          let arcAnimationGroup: CAAnimationGroup = CAAnimationGroup()          arcAnimationGroup.animations = [waveAnimationPre, waveAnimationLow, waveAnimationMid, waveAnimationHigh, waveAnimationComplete]          arcAnimationGroup.duration = waveAnimationComplete.beginTime + waveAnimationComplete.duration          arcAnimationGroup.fillMode = kCAFillModeForwards          arcAnimationGroup.removedOnCompletion = false          addAnimation(arcAnimationGroup, forKey: nil)      }</code></pre>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/6364532c429754af552b5cac99c4ae22.gif"></p>    <p>找几个点控制水波形状,画出贝塞尔曲线即可,到这里基本就完成了。接下来最后一步,放大,并弹出<code>Welcome</code></p>    <pre>  <code class="language-objectivec">    func expandView() {          backgroundColor = UIColor.colorWithHexString("#40e0b0")          frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth,                             frame.origin.y - blueRectangleLayer.lineWidth,                             frame.size.width + blueRectangleLayer.lineWidth * 2,                             frame.size.height + blueRectangleLayer.lineWidth * 2)          layer.sublayers = nil            UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {              self.frame = self.parentFrame              }, completion: { finished in                  self.delegate?.completeAnimation()          })        }</code></pre>    <p>放大完以后设置代理,然后在主的<code>vc</code>中添加<code>Welcome</code>这个<code>Label</code></p>    <pre>  <code class="language-objectivec">    // MARK: -      // MARK: AnimationViewDelegate      func completeAnimation() {          // 1          animationView.removeFromSuperview()          view.backgroundColor = UIColor.colorWithHexString("#40e0b0")            // 2          let label: UILabel = UILabel(frame: view.frame)          label.textColor = UIColor.whiteColor()          label.font = UIFont(name: "HelveticaNeue-Thin", size: 50.0)          label.textAlignment = NSTextAlignment.Center          label.text = "Welcome"          label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25)          view.addSubview(label)            // 3          UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: UIViewAnimationOptions.CurveEaseInOut,animations: ({                    label.transform = CGAffineTransformScale(label.transform, 4.0, 4.0)              }), completion: { finished in                  self.addTouchButton()          })        }</code></pre>    <p>到现在为止,动画全部完成</p>    <p style="text-align: center;"><img alt="iOS复杂动画之抽丝剥茧(Objective-C & Swift)" src="https://simg.open-open.com/show/98823f8198bd91ade9664d843e59ac7c.gif"></p>    <h2>六、最后</h2>    <p>同样,还是提供了两个版本(Objective-C & Swift),你可以在 <a href="/misc/goto?guid=4959674479502347717">这里</a> 查看源码!<br> 一直对动画比较感兴趣,希望多研究多深入,有什么意见或者建议的话,可以留言或者私信,如果觉得还好的话,请<code>star</code>支持,谢谢!</p>    <p><br> 来自:https://github.com/Loveway/HWLoadingAnimation</p>    <p> </p>