美文网首页
Swift - 实现一个波浪动画效果(CADisplayLink

Swift - 实现一个波浪动画效果(CADisplayLink

作者: 门前的那颗樱桃树 | 来源:发表于2019-03-25 11:12 被阅读0次

  说到定时器我们常常会想到 Timer,我之前也写过不少相关文章。其实除了 Timer,还有 CADisplayLink 这个定时器。不同于 Timer 需要在初始化时设置一个定时周期,CADisplayLink 是一个同步屏幕刷新频率的定时器。

1、CADisplayLink 的介绍

(1)由于 iOS 设备的屏幕刷新频率是固定的 60Hz,因此 CADisplayLink 的 selector 默认调用周期也是是每秒 60 次(这个周期可以通过 frameInterval 属性设置)。

(2)不同于 Timer 因为阻塞问题会造成触发时间的延迟,CADisplayLink 在正常情况下会在每次刷新结束时被调用,因此精确度相当高。适合做 UI 的不停重绘,比如自定义动画引擎或者视频播放的渲染。

2、样例效果图

(1)下面使用 CADisplayLink 实现一个波浪效果。波浪会从右往左不断地移动,同时上方的文字也会随之上下浮动。

(2)波浪曲线其实是一条正弦曲线,整个动画的实现原理是在每次屏幕刷新时,改变曲线的偏移量再重新绘制,从而实现波浪起伏的效果。

原文:Swift - 实现一个波浪动画效果(CADisplayLink定时器的使用样例) 原文:Swift - 实现一个波浪动画效果(CADisplayLink定时器的使用样例) 原文:Swift - 实现一个波浪动画效果(CADisplayLink定时器的使用样例)

3、样例代码

ViewController.swift(主视图控制器)
import UIKit
 
class ViewController: UIViewController {
 
    override func viewDidLoad() {
        super.viewDidLoad()
         
        // 创建文本标签
        let label = UILabel()
        label.text = "正在加载中......"
        label.textColor = .white
        label.textAlignment = .center
        label.frame = CGRect(x: (screenWidth() * 0.5 - 100), y: 0, width: 200, height: 50)
         
        // 创建波浪视图
        let waveView = WaveView(frame: CGRect(x: 0, y: 0, width: screenWidth(),
                                              height: 130))
        // 波浪动画回调
        waveView.closure = {centerY in
            // 同步更新文本标签的y坐标
            label.frame.origin.y = waveView.frame.height + centerY - 55
        }
         
        // 添加两个视图
        self.view.addSubview(waveView)
        self.view.addSubview(label)
         
        // 开始播放波浪动画
        waveView.startWave()
    }
     
    // 返回当前屏幕宽度
    func screenWidth() -> CGFloat {
        return UIScreen.main.bounds.size.width
    }
}
WaveView.swift(自定义的波浪视图组件)
import UIKit
import QuartzCore
 
// 波浪曲线动画视图
// 波浪曲线公式:y = h * sin(a * x + b); h: 波浪高度, a: 波浪宽度系数, b: 波的移动
class WaveView: UIView {
    // 波浪高度h
    var waveHeight: CGFloat = 7
    // 波浪宽度系数a
    var waveRate: CGFloat = 0.01
    // 波浪移动速度
    var waveSpeed: CGFloat = 0.05
    // 真实波浪颜色
    var realWaveColor: UIColor = UIColor(55, 153, 249)
    // 阴影波浪颜色
    var maskWaveColor: UIColor = UIColor(55, 153, 249, 0.3)
    // 波浪位置(默认是在下方)
    var waveOnBottom = true
    // 波浪移动回调
    var closure: ((_ centerY: CGFloat)->())?
     
    // 定时器
    private var displayLink: CADisplayLink?
    // 真实波浪
    private var realWaveLayer: CAShapeLayer?
    // 阴影波浪
    private var maskWaveLayer: CAShapeLayer?
    // 波浪的偏移量
    private var offset: CGFloat = 0
     
    // 视图初始化
    override init(frame: CGRect) {
        super.init(frame: frame)
        initWaveParame()
    }
     
    // 视图初始化
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initWaveParame()
    }
     
    // 组件初始化
    private func initWaveParame() {
        // 真实波浪配置
        realWaveLayer = CAShapeLayer()
        var frame = bounds
        realWaveLayer?.frame.origin.y = frame.size.height - waveHeight
        frame.size.height = waveHeight
        realWaveLayer?.frame = frame
         
        // 阴影波浪配置
        maskWaveLayer = CAShapeLayer()
        maskWaveLayer?.frame.origin.y = frame.size.height - waveHeight
        frame.size.height = waveHeight
        maskWaveLayer?.frame = frame
         
        layer.addSublayer(maskWaveLayer!)
        layer.addSublayer(realWaveLayer!)
    }
     
    // 开始播放动画
    func startWave() {
        // 开启定时器
        displayLink = CADisplayLink(target: self, selector: #selector(self.wave))
        displayLink!.add(to: RunLoop.current, forMode: RunLoop.Mode.common)
    }
     
    // 停止播放动画
    func endWave() {
        // 结束定时器
        displayLink!.invalidate()
        displayLink = nil
    }
     
    // 定时器响应(每一帧都会调用一次)
    @objc func wave() {
        // 波浪移动的关键:按照指定的速度偏移
        offset += waveSpeed
         
        // 起点y坐标(没有波浪的一侧)
        let startY = self.waveOnBottom ? 0 : frame.size.height
         
        let realPath = UIBezierPath()
        realPath.move(to: CGPoint(x: 0, y: startY))
         
        let maskPath = UIBezierPath()
        maskPath.move(to: CGPoint(x: 0, y: startY))
         
        var x = CGFloat(0)
        var y = CGFloat(0)
        while x <= bounds.size.width {
            // 波浪曲线
            y = waveHeight * sin(x * waveRate + offset)
            // 如果是下波浪还要加上视图高度
            let realY = y + (self.waveOnBottom ? frame.size.height : 0)
            let maskY = -y + (self.waveOnBottom ? frame.size.height : 0)
             
            realPath.addLine(to: CGPoint(x: x, y: realY))
            maskPath.addLine(to: CGPoint(x: x, y: maskY))
             
            // 增量越小,曲线越平滑
            x += 0.1
        }
         
        let midX = bounds.size.width * 0.5
        let midY = waveHeight * sin(midX * waveRate + offset)
         
        if let closureBack = closure {
            closureBack(midY)
        }
        // 回到起点对侧
        realPath.addLine(to: CGPoint(x: frame.size.width, y: startY))
        maskPath.addLine(to: CGPoint(x: frame.size.width, y: startY))
         
        // 闭合曲线
        maskPath.close()
         
        // 把封闭图形的路径赋值给CAShapeLayer
        maskWaveLayer?.path = maskPath.cgPath
        maskWaveLayer?.fillColor = maskWaveColor.cgColor
         
        realPath.close()
        realWaveLayer?.path = realPath.cgPath
        realWaveLayer?.fillColor = realWaveColor.cgColor
    }
}
UIColor+.swift(UIColor 扩展)
import UIKit
 
//UIColor扩展
extension UIColor {
     
    //使用rgb方式生成自定义颜色
    convenience init(_ r : CGFloat, _ g : CGFloat, _ b : CGFloat) {
        let red = r / 255.0
        let green = g / 255.0
        let blue = b / 255.0
        self.init(red: red, green: green, blue: blue, alpha: 1)
    }
     
    //使用rgba方式生成自定义颜色
    convenience init(_ r : CGFloat, _ g : CGFloat, _ b : CGFloat, _ a : CGFloat) {
        let red = r / 255.0
        let green = g / 255.0
        let blue = b / 255.0
        self.init(red: red, green: green, blue: blue, alpha: a)
    }
}

源码下载

附:上波浪样式

4、效果图

  波浪视图组件WaveView还提供了个 waveOnBottom 属性,我们将其设置为 false 即可让波浪显示在组件视图的上侧。

image.png

5、样例代码

import UIKit
 
class ViewController: UIViewController {
 
    override func viewDidLoad() {
        super.viewDidLoad()
         
        // 创建文本标签
        let label = UILabel()
        label.text = "正在加载中......"
        label.textColor = UIColor(55, 153, 249)
        label.textAlignment = .center
        label.frame = CGRect(x: (screenWidth() * 0.5 - 100), y: 0, width: 200, height: 50)
         
        // 创建波浪视图
        let waveView = WaveView(frame: CGRect(x: 0, y: screenHeight() - 60,
                                              width: screenWidth(), height: 60))
        // 波浪显示在上方
        waveView.waveOnBottom = false
        // 波浪动画回调
        waveView.closure = {centerY in
            // 同步更新文本标签的y坐标
            label.frame.origin.y = waveView.frame.origin.y + centerY - 60
        }
         
        // 添加两个视图
        self.view.addSubview(waveView)
        self.view.addSubview(label)
         
        // 开始播放波浪动画
        waveView.startWave()
    }
     
    // 返回当前屏幕宽度
    func screenWidth() -> CGFloat {
        return UIScreen.main.bounds.size.width
    }
     
    // 返回当前屏幕高度
    func screenHeight() -> CGFloat {
        return UIScreen.main.bounds.size.height
    }
}

原文链接:Swift - 实现一个波浪动画效果(CADisplayLink定时器的使用样例)

相关文章

网友评论

      本文标题:Swift - 实现一个波浪动画效果(CADisplayLink

      本文链接:https://www.haomeiwen.com/subject/qbcvvqtx.html