iOS 利用加速计实现多个波浪线

需求

要求根据手机摇晃程度(加速计),来实现多个波浪线动画,要求两端低,中间高。
实现效果:


1.gif

工作原理

正弦曲线公式:y=Asin(ωx+φ)+k
A :振幅,曲线最高位和最低位的距离
ω :角速度,用于控制周期大小,单位x中起伏的个数
K :偏距,曲线上下偏移量
φ :初相,曲线左右偏移量
曲线图如下:


iOS 利用加速计实现多个波浪线_第1张图片
曲线图.png

现在有一根正弦曲线出来了,要产生动画只要实时改变φ,就会有左右滚动的效果,多个曲线就是原始φ不同,多个曲线峰值高低则改变A ,波浪线要中间高两边低其实也简单,只要再乘以半个周期的标准正弦曲线。


iOS 利用加速计实现多个波浪线_第2张图片
image.png

看了图片相信恍然大悟了吧

代码实现

import UIKit
import CoreMotion

class SportWaveLineView: UIView {

    var lastAccelera: Double = 0.0
    let path1 = UIBezierPath()
    let path2 = UIBezierPath()
    let path3 = UIBezierPath()
    let path4 = UIBezierPath()
    let layer1 = CAShapeLayer()
    let layer2 = CAShapeLayer()
    let layer3 = CAShapeLayer()
    let layer4 = CAShapeLayer()
    var offset: Double = 0.0
    
    lazy var noMotionlabel: UILabel = {
       
        let noMotionlabel = UILabel(frame: .zero)
        noMotionlabel.text = """
        鹰和鹰将利用手机传感器来记录你的跑步
        请确认手机可以感受到迈步或者摆臂,以减少误差
        """
        noMotionlabel.numberOfLines = 2
        noMotionlabel.textAlignment = NSTextAlignment.center
        noMotionlabel.font = UIFont.systemFont(ofSize: 12)
        noMotionlabel.textColor = UIColor(white: 1.0, alpha: 0.3)
        noMotionlabel.backgroundColor = UIColor(hexString: "#393E4C")
        addSubview(noMotionlabel)
        noMotionlabel.snp.makeConstraints({ (make) in
            make.center.equalToSuperview()
            make.left.equalToSuperview().offset(30)
            make.right.equalToSuperview().offset(-30)
        })
        return noMotionlabel
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        layer1.strokeColor = UIColor(white: 1.0, alpha: 0.3).cgColor
        layer1.fillColor = UIColor.clear.cgColor
        layer1.lineWidth = 0.5
        layer.addSublayer(layer1)
        
        layer2.strokeColor = UIColor(white: 1.0, alpha: 0.3).cgColor
        layer2.fillColor = UIColor.clear.cgColor
        layer2.lineWidth = 0.5
        layer.addSublayer(layer2)
        
        layer3.strokeColor = UIColor(white: 1.0, alpha: 0.3).cgColor
        layer3.fillColor = UIColor.clear.cgColor
        layer3.lineWidth = 0.5
        layer.addSublayer(layer3)
        
        layer4.strokeColor = UIColor(white: 1.0, alpha: 0.3).cgColor
        layer4.fillColor = UIColor.clear.cgColor
        layer4.lineWidth = 0.5
        layer.addSublayer(layer4)
        
        //判断加速计是否可用,不能就一根直线
        if !SportTrackingManager.shareInstance.accelerometerManager.isAccelerometerAvailable {
            path1.move(to: CGPoint(x: 0, y: self.bounds.height / 2))
            path1.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 2))
            layer1.path = path1.cgPath
            return
        }
        SportTrackingManager.shareInstance.accelerometerManager.accelerometerUpdateInterval = 1 / 15
        SportTrackingManager.shareInstance.accelerometerManager.startAccelerometerUpdates(to: OperationQueue.main) { (accelerometerData, error) in
            let acceleration = accelerometerData?.acceleration
            //            print(acceleration?.x, acceleration?.y, acceleration?.z)
            let accelera = sqrt(pow(acceleration!.x, 2) + pow(acceleration!.y, 2) + pow(acceleration!.z, 2))
            if accelera < 1.05 && accelera > 0.9 {
                return
            }
            if accelera > self.lastAccelera {
                self.lastAccelera = accelera
                self.lastAccelera *= 1.4
            }
            if accelera <= 0.9 {
                let lowTemp = 0.9 - accelera
                let highTemp = self.lastAccelera - 1.05
                if lowTemp > highTemp {
                    self.lastAccelera = 1.05 + lowTemp
                    self.lastAccelera *= 1.4
                }
            }
            if self.lastAccelera > 6 {
                self.lastAccelera = 6
            }
        }
        //根据手机刷新频率刷新曲线
        let display = CADisplayLink(target: self, selector: #selector(drawWaveLine))
        display.add(to: RunLoop.current, forMode: .commonModes)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    @objc func drawWaveLine() {
        
        path1.removeAllPoints()
        path2.removeAllPoints()
        path3.removeAllPoints()
        path4.removeAllPoints()
        path1.move(to: CGPoint(x: 0, y: self.bounds.height / 2))
        
        path2.move(to: CGPoint(x: 0, y: self.bounds.height / 2))
        path2.addLine(to: CGPoint(x: 5, y: self.bounds.height / 2))
        
        path3.move(to: CGPoint(x: 0, y: self.bounds.height / 2))
        path3.addLine(to: CGPoint(x: 10, y: self.bounds.height / 2))
        path4.move(to: CGPoint(x: 0, y: self.bounds.height / 2))
        path4.addLine(to: CGPoint(x: 15, y: self.bounds.height / 2))
        for x in 0...Int(bounds.width) {
            
            let y =  9 * self.lastAccelera * sin(Double.pi / Double(UIScreen.main.bounds.width / 6) * Double(x) + offset) * (sin(Double(x) * Double.pi / Double(UIScreen.main.bounds.width)))
            path1.addLine(to: CGPoint(x: Double(x), y: y + Double(bounds.height / 2)))
            
            if CGFloat(x) < bounds.width - 5 {
                
                let y1 =  8 * self.lastAccelera * sin(Double.pi / Double(UIScreen.main.bounds.width / 6) * Double(x) + offset - 0.2) * (sin(Double(x) * Double.pi / Double(UIScreen.main.bounds.width - 5)))
                path2.addLine(to: CGPoint(x: Double(x) + 5, y: y1 + Double(bounds.height / 2)))
            }
            
            if CGFloat(x) < bounds.width - 10 {
                
                let y2 =  7 * self.lastAccelera * sin(Double.pi / Double(UIScreen.main.bounds.width / 6) * Double(x) + offset - 0.4) * (sin(Double(x) * Double.pi / Double(UIScreen.main.bounds.width - 10)))
                path3.addLine(to: CGPoint(x: Double(x) + 10, y: y2 + Double(bounds.height / 2)))
            }
            
            if CGFloat(x) < bounds.width - 15 {
                
                let y3 =  6 * self.lastAccelera * sin(Double.pi / Double(UIScreen.main.bounds.width / 6) * Double(x) + offset - 0.6) * (sin(Double(x) * Double.pi / Double(UIScreen.main.bounds.width - 15)))
                path4.addLine(to: CGPoint(x: Double(x) + 15, y: y3 + Double(bounds.height / 2)))
                
            }
        }
        
        path1.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 2))
        path2.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 2))
        path3.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 2))
        path4.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 2))
        
        
        layer1.path = path1.cgPath
        layer2.path = path2.cgPath
        layer3.path = path3.cgPath
        layer4.path = path4.cgPath
        
        //没有加速就显示文案,波浪线归0
        if self.lastAccelera <= 0.1 {
            self.lastAccelera = 0.0
            self.noMotionlabel.isHidden = false
        } else {
            self.noMotionlabel.isHidden = true
            self.lastAccelera -= 0.08
        }
        //波浪线移动
        offset -= 0.18
        if offset < -60 * Double.pi {
            offset = 0
        }
        
    }

}

你可能感兴趣的:(iOS 利用加速计实现多个波浪线)