Swift实现水波纹效果

水波纹效果

水波纹.png

网上虽然很多水波纹动画效果实现的文章,但是我觉得有点讲解的不清楚,有点过于深化了。这篇我喜欢用笨方法让大家理解波纹曲线实现原理。

知识点

主要用到数学公式:正弦型函数解析式:y = A * sin(ωx+φ) + h
A:决定峰值(即纵向拉伸压缩的倍数),通俗点叫振幅。
h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)。
φ:初相位:决定波形与X轴位置关系或横向移动距离(左加右减),和波纹运动速度有关系。
ω:决定周期(最小正周期T=2π/|ω|),在实际中一般一个周期就是view的width,可以求出ω = 2 * π/view.bounds.width
x:就是每一个点x的坐标,你所看到的水波纹曲线实际是由系统一个一个点连接而成的。

分析

1.如果你想理解绘制原理,在开始学习的时候可以把A/h/ω 这几个值写成固定值,φ这个值和速度有关系,你可以给定一个初始速度从而得到每个时间段的相位值如:offsetX += waveSpeed。
2.先绘制单个波浪曲线有利益你分析实现原理从而实现二段或者其他样式的水波纹。
3.绘制类准备:CAShaperLayer和CADisplayLink(因为帧率每秒60FPS)。
4.去调节改变公式里面各个参数的值看波纹曲线变化,写出适合你项目的波纹曲线。

主要代码

 /**
     处理displaylink事件,比较要注意的是当循环结束添加line
     wavePath1.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
     wavePath1.addLine(to: CGPoint.init(x: 0, y: bounds.height))
     因为我的坐标系是自上而下的,因此注意最后两个点绘制坐标。
     */
    @objc private func handleTimer()
    {
        let wavePath1 = UIBezierPath()
        let wavePath2 = UIBezierPath()
        //振幅系数,振幅越大波峰越陡
        waveAmplitude = bounds.height * rate * 0.1
        //正弦型函数解析式:y = A * sin(ωx+φ) + h  ω 读alpha
        var y1 = (1 - rate) * bounds.height
        var y2 = y1
        wavePath1.move(to: CGPoint.init(x: 0, y: y1))
        wavePath2.move(to: CGPoint.init(x: 0, y: y2))
        //波长里面有几个波曲线可设置
        let a = waveCycle
        //当offsetX趋近CGFloat maxValue值时这个值应减减防止奔溃
        if offsetX >= CGFloat.greatestFiniteMagnitude {
            offsetX = offsetX - waveSpeed
        }else {
            offsetX = offsetX + waveSpeed
        }
        //峰值
        let h = (1 - rate) * bounds.height
        for x in 0...Int(bounds.width) {
            //控制波浪1和波浪2的速度不同形成动画效果
            let f1 =  offsetX / bounds.width * .pi
            let f2 =  0.75 * offsetX / bounds.width * .pi
            y1 = waveAmplitude * CGFloat(sin(Double(a * CGFloat(x) + f1))) + (h - 10)
            y2 = waveAmplitude * CGFloat(sin(Double(a * CGFloat(x) + f2))) + h
            wavePath1.addLine(to: CGPoint.init(x: CGFloat(x), y: y1))
            wavePath2.addLine(to: CGPoint.init(x: CGFloat(x), y: y2))
        }
        wavePath1.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
        wavePath1.addLine(to: CGPoint.init(x: 0, y: bounds.height))
        wavePath1.close()
        waveLayer1.path = wavePath1.cgPath
        
        wavePath2.addLine(to: CGPoint.init(x: bounds.width, y: bounds.height))
        wavePath2.addLine(to: CGPoint.init(x: 0, y: bounds.height))
        wavePath2.close()
        waveLayer2.path = wavePath2.cgPath
        
    }

总结

1.主要是要理解正弦函数:y = A * sin(ωx+φ) + h 这几个参数对波纹的影响,你基本都能知道原理了,封装各种样式的水波纹效果离你不远了。

2.同时也要注意我手机的坐标是自上而下,从左往右的,(0,0)、(width,0)、(height,0)和(width,height),在连接最后两个点的时候需要注意下。

源代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 类似淘宝的效果:  我们知道,计算机不可能绘制出一条完美的曲线,如果放大到像素的级别,可以看到这些曲线其实都是栅格...
    CholMay阅读 1,215评论 0 7
  • 废话不多说先上图,看看这个酷炫的下拉刷新动画: 然后自己动手研究了一下,下面讲讲实现原理。水波动画的关键点就是正余...
    real潘阅读 1,482评论 1 18
  • 深入理解傅里叶变换Mar 12, 2017 这原本是我在知乎上对傅立叶变换、拉普拉斯变换、Z变换的联系?为什么要进...
    价值趋势技术派阅读 5,824评论 2 2
  • 乞丐的冬天肯定特别难熬吧,冬夜那么长一分一秒都是难熬的。
    嘟嘟小妹LZ阅读 62评论 0 0
  • 今天,成成对我说:妈妈白天说的话都不是秘密,只有晚上说的话才是秘密。所以,我们晚上说的都是秘密,都不能随便告诉别人...
    成妹儿阅读 259评论 0 0