iOS开发:仿余额宝金额跳动效果

前面写了一个Android版,因为有属性动画,很简单几行代码就实现了。
iOS版复杂多了,当然也可能我没找到简单的方法。参考了开源项目 https://github.com/PigRiver/NumberJumpDemo,这个是用OC写的,我用Swift改写了一个版本。
说下原理:先确定使用的贝塞尔曲线, 确定两桢间的间隔,根据间隔和总时间计算得到总桢数,根据贝塞尔曲线算出每桢的时间和值,然后delay设置显示的string。

贝塞尔曲线可以在http://cubic-bezier.com/生成。我选了ease-in-out的效果,不过为了ease更明显,我改了下。

//存储贝塞尔曲线的点
import UIKit
class BezierPoint {
    let x:Float!
    let y:Float!
    init(x:Float,y:Float) {
        self.x = x
        self.y = y
    }
}

算点

import UIKit

class BezierCurve {
    //根据t(位置,0-1)取点
    class func PointOnCubicBezier(cp:[BezierPoint], t:Float) -> BezierPoint{
        var ax,bx,cx:Float
        var ay,by,cy:Float
        var tSquared,tCubed:Float
        
        /*計算多項式係數*/
        
        cx = 3.0 * (cp[1].x - cp[0].x);
        bx = 3.0 * (cp[2].x - cp[1].x) - cx;
        ax = cp[3].x - cp[0].x - cx - bx;
        
        cy = 3.0 * (cp[1].y - cp[0].y);
        by = 3.0 * (cp[2].y - cp[1].y) - cy;
        ay = cp[3].y - cp[0].y - cy - by;
        /*計算位於參數值t的曲線點*/
        tSquared = t * t;
        tCubed = tSquared * t;
        var x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
        var y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
        return BezierPoint(x: x, y: y)
    }
}

使用CATextLayer来渲染

import UIKit

class CountLayer: CATextLayer {
    //每帧间隔
    let durationPerFrame:NSTimeInterval = 20.0;
    //总时间
    var durationTotal:NSTimeInterval = 1500.0
    //开始值
    var startNumber:Float!
    //结束值
    var endNumber:Float!
    //总点数
    var pointNumber:Int!
    //当前画的点
    var indexCurrent:Int
    var lastTime:Float!
    //开始点
    let startPoint:BezierPoint = BezierPoint(x: 0, y: 0)
    //两个bezier点,http://cubic-bezier.com/ 可以生成
    let controlPoint1:BezierPoint = BezierPoint(x: 0.0, y: 0.78)
    var controlPoint2:BezierPoint = BezierPoint(x: 0.0, y: 0.98)
    //结束点,固定1,1
    var endPoint:BezierPoint = BezierPoint(x: 1, y: 1)
    //计算得到的所有点
    var allPoints:NSMutableArray!
    
    override init!() {
         indexCurrent = 0
        super.init()
        initDate()
    }

    required init(coder aDecoder: NSCoder) {
         indexCurrent = 0
        super.init(coder: aDecoder)
       initDate()
    }
    
    private func initDate() {
        allPoints = NSMutableArray()
       
        lastTime = 0
    }
    
    //动画跳动展示
    func showNumberWithAnimation(duration:NSTimeInterval, startNumber:Float, endNumber:Float) {
        indexCurrent = 0
        lastTime = 0
        self.durationTotal = duration
        self.startNumber = startNumber
        self.endNumber = endNumber
        initBezierPoint()
        changeNumberBySelector()
    }
    
    //初始化h
    private func initBezierPoint() {
        //总共多少点
        pointNumber = Int(ceil(durationTotal/durationPerFrame))
        var bezierPoints:[BezierPoint] = [startPoint, controlPoint1, controlPoint2, endPoint]
        allPoints.removeAllObjects()
        //读出每点的时间和值
        for index in 0 ..< pointNumber {
            var t = Float(index)/Float(pointNumber)
            var point:BezierPoint = BezierCurve.PointOnCubicBezier(bezierPoints, t: t)
            var durationTime = point.x * Float(durationTotal)
            var value:Float = point.y * (endNumber - startNumber) + startNumber;
            allPoints.addObject(NSArray(array: [durationTime,value]))
        }
        
        
    }
    func changeNumberBySelector() {
        if(indexCurrent >= pointNumber) {
            self.string = NSString(format: "%.2f", endNumber)
        }else{
            var currentPoint: NSArray = allPoints.objectAtIndex(indexCurrent) as NSArray
            var value = currentPoint.objectAtIndex(1) as Float
            var currentTime = currentPoint.objectAtIndex(0) as Float
            
            ++indexCurrent
            var timeDuration = currentTime - lastTime
            lastTime = currentTime;
            self.string = NSString(format: "%.2f", value)
            
            //延迟调用changeNumberBySelector方法,重新设string
            let delay = durationPerFrame * Double(NSEC_PER_MSEC)
            var time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
            dispatch_after(time, dispatch_get_main_queue(), {
                NSThread.detachNewThreadSelector(Selector("changeNumberBySelector"), toTarget:self, withObject: nil)
            })

        }
    }
    
}

使用:

    //赚到的钱
    var earnLayer:CountLayer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
               
        earnLayer = CountLayer()
        earnLayer.frame = CGRectMake(85, 37, 151, 80)
        earnLayer.string = "asdf"
        earnLayer.fontSize = 30
        earnLayer.contentsScale = 2
        earnLayer.alignmentMode = "center"
        topBgView.layer.addSublayer(earnLayer)
       
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func test(sender: AnyObject) {
        earnLayer.showNumberWithAnimation(1500, startNumber: 0.0, endNumber: 999999.65)
        
    }

© 2015, 冰冻鱼. 请尊重作者劳动成果,复制转载保留本站链接! 应用开发笔记