iOS動(dòng)畫進(jìn)階-手摸手教你寫ShineButton動(dòng)畫

前段時(shí)間在github上看見一個(gè)非常nice的動(dòng)畫效果,可惜是安卓的,想著用swift寫一個(gè)iOS版的,下下來源代碼研究了一下,下面是我寫代碼的心路歷程

先上圖和demo的地址

分析動(dòng)畫過程

剛開始看的時(shí)候感覺這個(gè)動(dòng)畫很炫酷,實(shí)現(xiàn)起來應(yīng)該挺復(fù)制的,后來我將gif圖逐一分解,大致瀏覽了一下安卓的實(shí)現(xiàn)過程,大致了解的實(shí)現(xiàn)的過程,下面是一些關(guān)鍵的動(dòng)畫步驟:

  1. 第一步是里面圖片的縮放動(dòng)畫,使用CALayer配合CAKeyframeAnimation來實(shí)現(xiàn);
  2. 第二步是是里面一個(gè)圓環(huán)逐漸變大的過程,使用CAShapeLayer配合CAKeyframeAnimation來實(shí)現(xiàn);
  3. 第三步是最外面一層太陽的擴(kuò)散效果同樣也使用CAShapeLayer配合CAKeyframeAnimation來實(shí)現(xiàn);
  4. 最后是閃爍和顏色變化的的效果,使用CABasicAnimationCADisplayLink來實(shí)現(xiàn)。

一、縮放動(dòng)畫的實(shí)現(xiàn)

這個(gè)實(shí)現(xiàn)的過程相對而言比較簡單,用CALayer做為mask來實(shí)現(xiàn)下圖心形的圖片,然后用CAKeyframeAnimation來實(shí)現(xiàn)動(dòng)畫,values的值為[0.4, 1, 0.9, 1],差值器模式為kCAAnimationCubic,下面是實(shí)現(xiàn)結(jié)果和關(guān)鍵代碼:

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")
}

二、圓環(huán)擴(kuò)散動(dòng)畫的實(shí)現(xiàn)

首先圓環(huán)我們用CAShapeLayer來繪制一個(gè)圓環(huán),然后通過CAKeyframeAnimation來改變圓環(huán)的path就可以了,下面是實(shí)現(xiàn)結(jié)果和關(guān)鍵代碼:

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")
}

三、太陽的擴(kuò)散效果實(shí)現(xiàn)

首先我們得先算出每個(gè)太陽的位置和將要擴(kuò)散到的位置,然后用CAShapeLayer繪制出太陽,用CAKeyframeAnimation實(shí)現(xiàn)擴(kuò)散的效果,下面是實(shí)現(xiàn)后的結(jié)果和關(guān)鍵代碼 :

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")
}

四、最后再將這些動(dòng)畫通過一定規(guī)律結(jié)合起來

上圖是將之前動(dòng)畫步驟組合起來后的效果,上面的一些代碼只是部分代碼,全部代碼可以去我的github地址上去下在瀏覽,如果大家喜歡可以點(diǎn)一個(gè)贊,有更好的想法也可以提出來,大家一起交流一下,最后謝謝大家閱讀~~

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

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

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