一系列用于创建动画颜色渐变的 SwiftUI View Modifier。
Shape
上直接使用 ViewModifier
View
上直接使用 ViewModifier
-- 当定义了自定义 Shape
时。可以通过 Swift Package Manager 使用 AnimatableGradients
。您可以将其作为依赖项添加到您的 Package.swift
文件中
let package = Package(
//...
dependencies: [
.package(url: "https://github.com/CypherPoet/AnimatableGradients", from: "0.1.1"),
],
//...
)
然后,在您想要使用它的任何地方简单地 import AnimatableGradients
。
由 AnimatableGradients
公开的 view modifier 都遵循其 AnimatableGradientModifier
协议。(您可以在此处查看实现。)
protocol AnimatableGradientModifier: AnimatableModifier {
associatedtype BaseShape: Shape
associatedtype GradientShapeStyle: ShapeStyle
var baseShape: BaseShape { get }
var startColors: [UIColor] { get }
var endColors: [UIColor] { get }
var completionPercentage: CGFloat { get set }
func gradientFill(in geometry: GeometryProxy) -> GradientShapeStyle
}
为了使用这些 modifier,AnimatableGradients
提供了以下在 View
和 Shape
上的扩展
(更详细的说明可以在下面找到。)
🔑 为了实现动画,您的包含视图需要使用一个绑定到 CGFloat
状态值的上述 modifier,该值的范围在 0.0 和 1.0 之间。
@State private var animationCompletion: CGFloat = 0.0
这是“动画完成”百分比,每个
AnimatableGradientModifier
将使用它来在每个渲染帧期间生成插值颜色值。
此外,相同的包含视图需要动画化动画完成的更改。一个常见的模式包括从包含视图的 onAppear
modifier 重复动画该值,以创建连续的动画效果。
.onAppear {
withAnimation(
Animation.easeInOut(duration: 1.0).repeatForever(autoreverses: true)
) {
self.animationCompletion = 1.0
}
}
总而言之,一个最小的功能示例可能如下所示
struct ContentView {
@State private var animationCompletion: CGFloat = 0.0
var body: some View {
RoundedRectangle(cornerRadius: 24)
.animatableLinearGradient(
startColors: [.red, .blue],
endColors: [.green, .yellow],
completionPercentage: animationCompletion
)
.onAppear {
withAnimation(
Animation.easeInOut(duration: 1.0).repeatForever(autoreverses: true)
) {
self.animationCompletion = 1.0
}
}
}
}
现在是一些更详细的信息...
extension Shape {
public func animatableLinearGradient (
startColors: [UIColor],
endColors: [UIColor],
startPoint: UnitPoint = .topLeading,
endPoint: UnitPoint = .bottomTrailing,
completionPercentage: CGFloat
) -> some View {
...
}
}
RoundedRectangle(cornerRadius: 24)
.animatableLinearGradient(
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .systemRed],
completionPercentage: animationCompletion
)
extension View {
public func animatableLinearGradient<BaseShape: Shape>(
baseShape: BaseShape,
startColors: [UIColor],
endColors: [UIColor],
startPoint: UnitPoint = .topLeading,
endPoint: UnitPoint = .bottomTrailing,
completionPercentage: CGFloat
) -> some View {
...
}
}
Color.clear
.animatableLinearGradient(
baseShape: RoundedRectangle(cornerRadius: 24),
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .systemRed],
completionPercentage: animationCompletion
)
extension Shape {
public func animatableRadialGradient (
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
startRadius: CGFloat? = nil,
endRadius: CGFloat? = nil,
completionPercentage: CGFloat
) -> some View {
...
}
}
RoundedRectangle(cornerRadius: 24)
.animatableRadialGradient(
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .black],
completionPercentage: animationCompletion
)
extension View {
public func animatableRadialGradient<BaseShape: Shape>(
baseShape: BaseShape,
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
startRadius: CGFloat? = nil,
endRadius: CGFloat? = nil,
completionPercentage: CGFloat
) -> some View {
...
}
}
Color.clear
.animatableRadialGradient(
baseShape: RoundedRectangle(cornerRadius: 24),
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .black],
completionPercentage: animationCompletion
)
extension Shape {
/// - Parameters:
/// - fullSpanStartAngle: Setting this property will cause the gradient to being at
/// the specified offset, and then span the entire circumference. (Default: `nil`)
public func animatableAngularGradient (
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
fullSpanStartAngle: Angle,
completionPercentage: CGFloat
) -> some View {
...
}
public func animatableAngularGradient (
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
startAngle: Angle = .zero,
endAngle: Angle = .radians(2 * .pi),
completionPercentage: CGFloat
) -> some View {
...
}
}
RoundedRectangle(cornerRadius: 24)
.animatableAngularGradient(
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .systemRed],
completionPercentage: animationCompletion
)
extension View {
/// - Parameters:
/// - fullSpanStartAngle: Setting this property will cause the gradient to being at
/// the specified offset, and then span the entire circumference. (Default: `nil`)
public func animatableAngularGradient<BaseShape: Shape>(
baseShape: BaseShape,
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
fullSpanStartAngle: Angle,
completionPercentage: CGFloat
) -> some View {
...
}
public func animatableAngularGradient<BaseShape: Shape>(
baseShape: BaseShape,
startColors: [UIColor],
endColors: [UIColor],
centerPoint: UnitPoint = .center,
startAngle: Angle = .zero,
endAngle: Angle = .radians(2 * .pi),
completionPercentage: CGFloat
) -> some View {
...
}
}
Color.clear
.animatableAngularGradient(
baseShape: RoundedRectangle(cornerRadius: 24),
startColors: [.systemPurple, .systemBlue, .systemGreen, .white],
endColors: [.systemGray, .systemPink, .systemOrange, .systemRed],
completionPercentage: animationCompletion
)
completionPercentage
值小于 0 或大于 1,它将自动被钳制到更接近的端点。startColors
和 endColors
数组在计算插值颜色之前会被压缩。 这意味着如果它们的大小不同,则较大的数组将被修剪为较小数组的大小,并且较大数组中多余的颜色将不会被使用。startColors
和 endColors
数组中的任何一个或两个都为空,则渐变将不会有任何颜色传递给它,并且它将显示为完全透明。非常欢迎对 AnimatableGradients
做出贡献。查看一些问题模板以获取更多信息。
AnimatableGradients 在 MIT 许可下可用。 有关更多信息,请参见LICENSE 文件。