iOS动画教你编写Slack的Loading动画进阶篇

2020-01-15 19:08:53王振洲

执行完后就跟上图一样的效果了~~~

动画分解

经过分析,可以将动画分为四个步骤:
 •画布的旋转动画,旋转两圈
 •线条由长变短的动画,更画布选择的动画一起执行,旋转一圈的时候结束
 •线条的位移动画,线条逐渐向中间靠拢,再画笔旋转完一圈的时候执行,两圈的时候结束
 •线条由短变长的动画,画布旋转完两圈的时候执行 

第一步画布旋转动画

这里我们使用CABasicAnimation基础动画,keyPath作用于画布的transform.rotation.z,以z轴为目标进行旋转,下面是效果图和代码:

iOS,Slack,Loading


//MARK: 动画步骤
  /**
   旋转的动画,旋转两圈
   */
  private func angleAnimation() {
    let angleAnimation         = CABasicAnimation.init(keyPath: "transform.rotation.z")
    angleAnimation.fromValue      = angle(-30)
    angleAnimation.toValue       = angle(690)
    angleAnimation.fillMode      = kCAFillModeForwards
    angleAnimation.removedOnCompletion = false
    angleAnimation.duration      = duration
    angleAnimation.delegate      = self
    layer.addAnimation(angleAnimation, forKey: "angleAnimation")
  }

第二步线条由长变短的动画

这里我们还是使用CABasicAnimation基础动画,keyPath作用于线条的strokeEnd属性,让strokeEnd从1到0来实现线条长短的动画,下面是效果图和代码:

iOS,Slack,Loading


/**
   线的第一步动画,线长从长变短
   */
  private func lineAnimationOne() {
    let lineAnimationOne         = CABasicAnimation.init(keyPath: "strokeEnd")
    lineAnimationOne.duration      = duration/2
    lineAnimationOne.fillMode      = kCAFillModeForwards
    lineAnimationOne.removedOnCompletion = false
    lineAnimationOne.fromValue      = 1
    lineAnimationOne.toValue       = 0
    for i in 0...3 {
      let lineLayer = lines[i]
      lineLayer.addAnimation(lineAnimationOne, forKey: "lineAnimationOne")
    }
  }

第三步线条的位移动画

这里我们也是使用CABasicAnimation基础动画,keyPath作用于线条的transform.translation.x和transform.translation.y属性,来实现向中间聚拢的效果,下面是效果图和代码:

iOS,Slack,Loading


/**
   线的第二步动画,线向中间平移
   */
  private func lineAnimationTwo() {
    for i in 0...3 {
      var keypath = "transform.translation.x"
      if i%2 == 1 {
        keypath = "transform.translation.y"
      }
      let lineAnimationTwo = CABasicAnimation.init(keyPath: keypath)
      lineAnimationTwo.beginTime = CACurrentMediaTime() + duration/2
      lineAnimationTwo.duration = duration/4
      lineAnimationTwo.fillMode = kCAFillModeForwards
      lineAnimationTwo.removedOnCompletion = false
      lineAnimationTwo.autoreverses = true
      lineAnimationTwo.fromValue = 0
      if i < 2 {
        lineAnimationTwo.toValue = lineLength/4
      }else {
        lineAnimationTwo.toValue = -lineLength/4
      }
      let lineLayer = lines[i]
      lineLayer.addAnimation(lineAnimationTwo, forKey: "lineAnimationTwo")
    }

    //三角形两边的比例
    let scale = (lineLength - 2*margin)/(lineLength - lineWidth)
    for i in 0...3 {
      var keypath = "transform.translation.y"
      if i%2 == 1 {
        keypath = "transform.translation.x"
      }
      let lineAnimationTwo = CABasicAnimation.init(keyPath: keypath)
      lineAnimationTwo.beginTime = CACurrentMediaTime() + duration/2
      lineAnimationTwo.duration = duration/4
      lineAnimationTwo.fillMode = kCAFillModeForwards
      lineAnimationTwo.removedOnCompletion = false
      lineAnimationTwo.autoreverses = true
      lineAnimationTwo.fromValue = 0
      if i == 0 || i == 3 {
        lineAnimationTwo.toValue = lineLength/4 * scale
      }else {
        lineAnimationTwo.toValue = -lineLength/4 * scale
      }
      let lineLayer = lines[i]
      lineLayer.addAnimation(lineAnimationTwo, forKey: "lineAnimationThree")
    }
  }