I am adding a border and blur effect on a shape during an animation (root view appearing on page) and seeing FPS drops when using an AngularGradient as the color of the border. I dont see the drops when using a normal color. The drop is noticeable when setting a device to "low battery" mode.
My code is set up as a custom view modifier, and the border is added as overlays in the shape of myShape
, where myShape
matches the shape of the view/shape being modified.
struct BorderBlur: ViewModifier {
var fillColor: some ShapeStyle {
AngularGradient(
colors: [.blue, .purple, .green, .blue],
center: .center)
.opacity(borderOpacity)
}
var myShape : some Shape {
RoundedRectangle(cornerRadius:36)
}
let borderWidth : CGFloat
let blurRadius: CGFloat
let borderOpacity: CGFloat
init(borderWidth: CGFloat, blurRadius: CGFloat, borderOpacity: CGFloat) {
self.borderWidth = borderWidth
self.blurRadius = blurRadius
self.borderOpacity = borderOpacity
}
public func body(content: Content) -> some View {
content
.overlay(
myShape
.stroke(lineWidth: borderWidth)
.fill(fillColor)
.padding(borderWidth)
)
.overlay(
myShape
.stroke(lineWidth: borderWidth)
.fill(fillColor)
.blur(radius: blurRadius)
.padding(borderWidth)
)
.overlay(
myShape
.stroke(lineWidth: borderWidth)
.fill(fillColor)
.blur(radius: blurRadius / 2)
.padding(borderWidth)
)
}
}
extension View {
public func borderBlur(borderWidth: CGFloat, blurRadius: CGFloat, borderOpacity: CGFloat) -> some View {
return modifier(BorderBlur(borderWidth: borderWidth, blurRadius: blurRadius, borderOpacity: borderOpacity))
}
}
struct MyRootView: View {
@State var didAppear = false
var borderWidth: CGFloat {
didAppear ? 3 : 0
}
var borderBlurRadius: CGFloat {
didAppear ? 10 : 0
}
var borderOpacity: CGFloat {
didAppear ? 1 : 0
}
var body: some View {
VStack {
RoundedRectangle(cornerRadius:36).fill(.clear)
.borderBlur(borderWidth: borderWidth, blurRadius: borderBlurRadius, borderOpacity: borderOpacity)
.frame(width: didAppear ? 300 : 100, height: didAppear ? 400 : 100)
.offset(y: didAppear ? 0 : -400)
}
.onAppear {
withAnimation(.linear(duration:2.0)) {
didAppear = true
}
}
}
}
If I change the fillColor to a standard color like Color.blue
, I see no FPS drops. Any ideas as to how to make the gradient render more efficiently? I tried drawingGroup()
, which does improve FPS, but it makes the blur look bad, and I also want to be able to animate blur size (+ border width, opacity).