iOS动画进阶 - 手摸手教你写ShineButton动画

morningcal 7年前
   <p>前段时间在github上看见一个非常nice的动画效果,可惜是安卓的,想着用swift写一个iOS版的,下下来源代码研究了一下,下面是我写代码的心路历程</p>    <p>先上图</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b7eafe503ab44315952cd27950bfdf09.gif"></p>    <h2>分析动画过程</h2>    <p>刚开始看的时候感觉这个动画很炫酷,实现起来应该挺复制的,后来我将gif图逐一分解,大致浏览了一下安卓的实现过程,大致了解的实现的过程,下面是一些关键的动画步骤:</p>    <ol>     <li>第一步是里面图片的缩放动画,使用 CALayer 配合 CAKeyframeAnimation 来实现;</li>     <li>第二步是是里面一个圆环逐渐变大的过程,使用 CAShapeLayer 配合 CAKeyframeAnimation 来实现;</li>     <li>第三步是最外面一层太阳的扩散效果同样也使用 CAShapeLayer 配合 CAKeyframeAnimation 来实现;</li>     <li>最后是闪烁和颜色变化的的效果,使用 CABasicAnimation 和 CADisplayLink 来实现。</li>    </ol>    <h2>一、缩放动画的实现</h2>    <p>这个实现的过程相对而言比较简单,用 CALayer 做为 mask 来实现下图心形的图片,然后用 CAKeyframeAnimation 来实现动画, values 的值为 [0.4, 1, 0.9, 1] ,差值器模式为 kCAAnimationCubic ,下面是实现结果和关键代码:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2a1ced7d000c1bf5dda249f92a2cc98e.gif"></p>    <pre>  <code class="language-objectivec">public func startAnim() {      let anim = CAKeyframeAnimation(keyPath: "transform.scale")      anim.duration  = animDuration      anim.values = [0.4, 1, 0.9, 1]      anim.calculationMode = kCAAnimationCubic      maskLayer.add(anim, forKey: "scale")  }</code></pre>    <h2>二、圆环扩散动画的实现</h2>    <p>首先圆环我们用 CAShapeLayer 来绘制一个圆环,然后通过 CAKeyframeAnimation 来改变圆环的 path 就可以了,下面是实现结果和关键代码:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/dbf7ca7e23451c4fa372933b2333ebfb.gif"></p>    <pre>  <code class="language-objectivec">public func startAnim() {      let anim = CAKeyframeAnimation(keyPath: "path")      anim.duration = params.animDuration * 0.1      let size = frame.size      let fromPath = UIBezierPath(arcCenter: CGPoint.init(x: size.width/2, y: size.height/2), radius: 1, startAngle: 0, endAngle: CGFloat(M_PI) * 2.0, clockwise: false).cgPath      let toPath = UIBezierPath(arcCenter: CGPoint.init(x: size.width/2, y: size.height/2), radius: size.width/2 * CGFloat(params.shineDistanceMultiple), startAngle: 0, endAngle: CGFloat(M_PI) * 2.0, clockwise: false).cgPath      anim.delegate = self      anim.values = [fromPath, toPath]      anim.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]      anim.isRemovedOnCompletion = false      anim.fillMode = kCAFillModeForwards      shapeLayer.add(anim, forKey: "path")  }</code></pre>    <h2>三、太阳的扩散效果实现</h2>    <p>首先我们得先算出每个太阳的位置和将要扩散到的位置,然后用 CAShapeLayer 绘制出太阳,用 CAKeyframeAnimation 实现扩散的效果,下面是实现后的结果和关键代码 :</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/208886f1e79bfaa720a553541c63b3d9.gif"></p>    <pre>  <code class="language-objectivec">public func startAnim() {      let radius = frame.size.width/2 * CGFloat(params.shineDistanceMultiple*1.4)      var startAngle: CGFloat = 0      let angle = CGFloat(M_PI*2/Double(params.shineCount)) + startAngle      if params.shineCount%2 != 0 {          startAngle = CGFloat(M_PI*2 - (Double(angle)/Double(params.shineCount)))      }      for i in 0..<params.shineCount {          let bigShine = shineLayers[i]          let bigAnim = getAngleAnim(shine: bigShine, angle: startAngle + CGFloat(angle)*CGFloat(i), radius: radius)          let smallShine = smallShineLayers[i]          var radiusSub = frame.size.width*0.15*0.66          if params.shineSize != 0 {              radiusSub = params.shineSize*0.66          }          let smallAnim = getAngleAnim(shine: smallShine, angle: startAngle + CGFloat(angle)*CGFloat(i) - CGFloat(params.smallShineOffsetAngle)*CGFloat(M_PI)/180, radius: radius-radiusSub)          bigShine.add(bigAnim, forKey: "path")          smallShine.add(smallAnim, forKey: "path")      }      let angleAnim = CABasicAnimation(keyPath: "transform.rotation")      angleAnim.duration = params.animDuration * 0.87      angleAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)      angleAnim.fromValue = 0      angleAnim.toValue = CGFloat(params.shineTurnAngle)*CGFloat(M_PI)/180      angleAnim.delegate = self      add(angleAnim, forKey: "rotate")  }</code></pre>    <h2>四、最后再将这些动画通过一定规律结合起来</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b2d4db4b7196a5f31e13db02c34f8e59.gif"></p>    <p>上图是将之前动画步骤组合起来后的效果,上面的一些代码只是部分代码,全部代码可以去我的 <a href="/misc/goto?guid=4959737406042823955" rel="nofollow,noindex">github地址</a> 上去下在浏览,如果大家喜欢可以点一个start,有更好的想法也可以提出来,大家一起交流一下,最后谢谢大家阅读~~</p>    <p> </p>    <p> </p>    <p> </p>