iOS 動畫十三:Stroke and Path Animations

這里我們實現(xiàn)一個 stroke and path animations 下拉刷新動畫。最后效果如下:

pull-to-refresh animation
1. 創(chuàng)建虛線圓
ovalShapeLayer.strokeColor = UIColor.white.cgColor
    ovalShapeLayer.fillColor = UIColor.clear.cgColor
    ovalShapeLayer.lineWidth = 4.0
    // 虛線組成為2單位實線3單位虛線
    ovalShapeLayer.lineDashPattern = [2, 3] // alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
    
    let refreshRadius = frame.size.height/2 * 0.8
    
    ovalShapeLayer.path = UIBezierPath(ovalIn: CGRect(
        x: frame.size.width/2 - refreshRadius,
        y: frame.size.height/2 - refreshRadius,
        width: 2 * refreshRadius,
        height: 2 * refreshRadius)).cgPath
    
    layer.addSublayer(ovalShapeLayer)
    
    let airplaneImage = UIImage(named: "airplane.png")!
    airplaneLayer.contents = airplaneImage.cgImage
    airplaneLayer.bounds = CGRect(x: 0.0, y: 0.0,width: airplaneImage.size.width, height: airplaneImage.size.height)
    
    airplaneLayer.position = CGPoint(
        x: frame.size.width/2 + frame.size.height/2 * 0.8, y: frame.size.height/2)
    layer.addSublayer(airplaneLayer)
    
    airplaneLayer.opacity = 0.0
2. 實現(xiàn) Path Animations
func beginRefreshing() {
    isRefreshing = true
    
    UIView.animate(withDuration: 0.3) {
      var newInsets = self.scrollView.contentInset
      newInsets.top += self.frame.size.height
      self.scrollView.contentInset = newInsets
    }
    
    let strokeStartAnimation = CABasicAnimation(keyPath: "strokeStart")
    strokeStartAnimation.fromValue = -0.5
    strokeStartAnimation.toValue = 1.0
    
    let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
    strokeEndAnimation.fromValue = 0.0
    strokeEndAnimation.toValue = 1.0
    
    let strokeAnimationGroup = CAAnimationGroup()
    strokeAnimationGroup.duration = 1.5
    strokeAnimationGroup.repeatDuration = 5.0
    strokeAnimationGroup.animations = [strokeStartAnimation, strokeEndAnimation]
    ovalShapeLayer.add(strokeAnimationGroup, forKey: nil)
    
    let flightAnimation = CAKeyframeAnimation(keyPath: "position")
    flightAnimation.path = ovalShapeLayer.path
    flightAnimation.calculationMode = kCAAnimationPaced
    
    let airplaneOrientationAnimation = CABasicAnimation(keyPath: "transform.rotation")
    airplaneOrientationAnimation.fromValue = 0
    airplaneOrientationAnimation.toValue = 2.0 * .pi
    
    let flightAnimationGroup = CAAnimationGroup()
    flightAnimationGroup.duration = 1.5
    flightAnimationGroup.repeatDuration = 5.0
    flightAnimationGroup.animations = [flightAnimation,airplaneOrientationAnimation]
    airplaneLayer.add(flightAnimationGroup, forKey: nil)
  }

func endRefreshing() {
    
    isRefreshing = false
    
    UIView.animate(withDuration: 0.3, delay:0.0, options: .curveEaseOut,
      animations: {
        var newInsets = self.scrollView.contentInset
        newInsets.top -= self.frame.size.height
        self.scrollView.contentInset = newInsets
      },
      completion: {_ in
        //finished
      }
    )
  }
  
  func redrawFromProgress(_ progress: CGFloat) {
     ovalShapeLayer.strokeEnd = progress
     airplaneLayer.opacity = Float(progress)
  }


3. strokeStart和strokeEnd 注釋

可以把strokeStart理解成一個橡皮擦,如果我們讓strokeStart從0到1的話 那么這個線就會被從(0,0)一直檫除到(100,0)

可以把strokeEnd理解為一個畫筆,strokeEnd從0 動畫到 1 那么動畫表現(xiàn)為線越來越長。

eg. 對勾動畫

先把路徑用貝賽爾曲線畫出來,然后用strokeEnd做動畫:

#pragma mark -- 成功的路徑
-(CGPathRef)getSuccessPath{
    
    // 圓
    UIBezierPath *ciclePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2,self.frame.size.height/2) radius:0.8*self.currentSize.width/2 startAngle:M_PI * 3 / 2 endAngle:M_PI * 7 / 2 clockwise:YES];
    // 對勾
    CGFloat W = self.frame.size.width *0.9;
    CGFloat H = self.frame.size.height*0.9;
    UIBezierPath *subPath  = [UIBezierPath bezierPath];
    [subPath moveToPoint:CGPointMake(W/4,H/2)];
    [subPath addLineToPoint:CGPointMake(W/2,(H/2)+ H/4)];
    [subPath addLineToPoint:CGPointMake(W-(W/8),H/4)];
    
    // 添加到路徑上 由于CAShapeLayer只能添加一條路徑  幸好貝賽爾曲線有個appendPath: 方法 可以讓我們畫多條路徑
    [ciclePath appendPath:subPath];
    return ciclePath.CGPath;
}

#pragma mark -- 成功的動畫
- (CABasicAnimation*)successAnimtion{
    
    // 第一個滿圓旋轉(zhuǎn)
    CABasicAnimation *aniamtion1 = [CABasicAnimation animation];
    aniamtion1.keyPath = @"strokeEnd";
    aniamtion1.fromValue = @0;
    aniamtion1.toValue = @1;
    aniamtion1.duration = 1.5;
    
    // 這個是緩沖函數(shù)
    // 可以自定義
    //
//    aniamtion1.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1 :1 :1 :1];
    
    aniamtion1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    aniamtion1.fillMode = kCAFillModeBackwards;
    return aniamtion1;
   }

demo下載

參考:

  1. strokeStart和strokeEnd

感謝 @謝微一直都得踢足球

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容