SwiftUI学习(6)-SwiftUI动画入门Part3
2021, Dec 04
SwiftUI动画入门Part 3-给自定义Shape添加动画
本文将介绍如何绘制上图中的正弦波动画。
关于自定义Shape,可以参考之前文章:
自定义正弦波Shape
正弦图形函数:y = sin(x * 波长)* 振幅, 定义波长等于 1/4的宽度, 振幅为1/3高度,原点(x: -波长,y: 1/2 高度),WaveShape
代码如下:
struct WaveShape: Shape{
func path(in rect: CGRect) -> Path {
var points = [CGPoint]()
let h = rect.height / 2
// 振幅
let amplitude = rect.height / 3
// 波长
let wavelength = rect.width / 4
for i in 0 ..< Int(rect.width) {
let x = CGFloat(i) - wavelength
let y = sin(2 * CGFloat.pi / wavelength * x) * amplitude + h
points.append(CGPoint(x: CGFloat(x), y: y))
}
return Path{ path in
path.move(to: points.first!)
points.forEach { p in
path.addLine(to: p)
}
path.addEllipse(in: CGRect(origin: points.last!.applying(CGAffineTransform(translationX: -5, y: -5)), size: CGSize(width: 10, height: 10)))
}
}
}
在view 中显示WaveShape
并上颜色:
var body: some View {
WaveShape()
.stroke(LinearGradient(colors: [Color(hue: 13 / 360, saturation: 0.79, brightness: 0.97), Color(hue: 40 / 360, saturation: 0.66, brightness: 0.93)], startPoint: UnitPoint(x: 0, y: 0.5), endPoint: UnitPoint(x: 1, y: 0.5)), lineWidth: 7)
}
得到一个静态正弦波页面:
给正弦波添加动画
SwiftUI动画的背后是一个Animatable
的协议, Animatable
大概张这样:
protocol Animatable{
associatedtype AnimatableData : VectorArithmetic
var animatableData: AnimatableData
}
当使用View进行动画的时候, SwiftUI会多出生成改View,并给animatableData
不同值。
Shape
协议继承了Animatable
, 通过实现animatableData
就可以实现动画了。
在WaveShape
添加一个time
属性,并实现Animatable
协议,将time属性作为动画参数:
...
var time: CGFloat
var animatableData: CGFloat{
get{
return time
}
set{
time = newValue
}
}
...
修改path
函数,将绘制和time
关联起来:
...
// 每个点y根据时间偏移
let y = sin(2 * CGFloat.pi / wavelength * x + wavelength * time) * a + h
...
在显示正弦波的View修改如下:
@State var animation: Bool = false
var body: some View {
WaveShape(time: self.animation ? 1: 0)
.stroke(LinearGradient(colors: [Color(hue: 13 / 360, saturation: 0.79, brightness: 0.97), Color(hue: 40 / 360, saturation: 0.66, brightness: 0.93)], startPoint: UnitPoint(x: 0, y: 0.5), endPoint: UnitPoint(x: 1, y: 0.5)), lineWidth: 7)
.onAppear {
withAnimation(.linear(duration: 60).repeatForever(autoreverses: false)) {
self.animation.toggle()
}
}
}
运行效果就如一开始那样: