iOS实现蛇形渐变边框动画遇阻,求可行解决方案
蛇形渐变边框动画实现问题
我想实现蛇形渐变边框动画,试过UIKit的CAGradientLayer结合CAKeyframeAnimation方案,但效果变成了背景渐变而非边框;后来用SwiftUI写了代码,但动态修改宽高时渐变动画会失效。附上当前效果和预期效果,希望有人指出代码问题,或者提供可行的实现方案(SwiftUI或UIKit都可以)。
当前SwiftUI代码
struct GradientBorderAnimationView: View { @State var rotation:CGFloat = 0.0 @State var width: CGFloat = 330 @State var height: CGFloat = 100 var body: some View { ZStack{ RoundedRectangle(cornerRadius: 20, style: .continuous) .frame(width: width*2, height: height*2) .foregroundStyle(LinearGradient(gradient: Gradient(colors: [.white, .white, .blue]), startPoint: .top, endPoint: .bottom)) .rotationEffect(.degrees(rotation)) .mask { RoundedRectangle(cornerRadius: 20, style: .continuous) .stroke(lineWidth: 3) .frame(width: width, height: height) } } .ignoresSafeArea() .onAppear{ withAnimation(.linear(duration: 4).repeatForever(autoreverses: false)){ rotation = 360 } } } }
当前效果

预期效果

解决方案
SwiftUI修复方案
你的代码问题在于:动态宽高变化时,外层ZStack未同步更新尺寸,且渐变矩形与mask的关联逻辑在尺寸变动时出现断层。下面是修改后的代码,支持动态宽高且动画正常:
struct GradientBorderAnimationView: View { @State var progress: CGFloat = 0.0 @State var width: CGFloat = 330 @State var height: CGFloat = 100 var gradient: LinearGradient { LinearGradient( gradient: Gradient(colors: [.white, .blue, .white]), startPoint: .init(x: progress - 0.2, y: progress - 0.2), endPoint: .init(x: progress + 0.2, y: progress + 0.2) ) } var body: some View { RoundedRectangle(cornerRadius: 20, style: .continuous) .strokeBorder(gradient, lineWidth: 3) .frame(width: width, height: height) .onAppear { withAnimation(.linear(duration: 4).repeatForever(autoreverses: false)) { progress = 1.0 } } .animation(.default, value: width) .animation(.default, value: height) } }
此方案直接用strokeBorder绘制渐变边框,通过修改渐变的startPoint和endPoint实现蛇形流动效果,动态调整宽高时动画会自动适配。
UIKit可行方案
如果偏好UIKit实现,可以通过CAShapeLayer作为CAGradientLayer的mask,再给渐变层添加平移动画:
class GradientBorderView: UIView { private let gradientLayer = CAGradientLayer() private let borderLayer = CAShapeLayer() override init(frame: CGRect) { super.init(frame: frame) setupLayers() startAnimation() } required init?(coder: NSCoder) { super.init(coder: coder) setupLayers() startAnimation() } private func setupLayers() { // 配置渐变层 gradientLayer.colors = [UIColor.white.cgColor, UIColor.blue.cgColor, UIColor.white.cgColor] gradientLayer.startPoint = CGPoint(x: 0, y: 0) gradientLayer.endPoint = CGPoint(x: 1, y: 1) gradientLayer.frame = bounds.insetBy(dx: -bounds.width, dy: -bounds.height) // 配置边框形状层作为mask borderLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath borderLayer.lineWidth = 3 borderLayer.fillColor = UIColor.clear.cgColor borderLayer.strokeColor = UIColor.black.cgColor gradientLayer.mask = borderLayer layer.addSublayer(gradientLayer) } override func layoutSubviews() { super.layoutSubviews() gradientLayer.frame = bounds.insetBy(dx: -bounds.width, dy: -bounds.height) borderLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath } private func startAnimation() { let animation = CABasicAnimation(keyPath: "transform.translation") animation.fromValue = CGPoint(x: -bounds.width, y: -bounds.height) animation.toValue = CGPoint(x: bounds.width, y: bounds.height) animation.duration = 4 animation.repeatCount = .infinity gradientLayer.add(animation, forKey: "gradientMove") } }
该方案通过放大渐变层并添加平移动画,配合形状层mask实现边框渐变流动,布局变化时layoutSubviews会自动更新路径和渐变层尺寸,保证动态宽高正常工作。
内容的提问来源于stack exchange,提问作者Arnab
相关产品推荐
相关产品推荐

