持续时间长的平滑动画 CAGradientLayer

Smooth animation CAGradientLayer with long duration

创建了具有鲜明颜色过渡的 CAGradientLayer。使用 CABasicAnimation 期间颜色过渡的位置发生变化。

一切正常,但如果我使用长持续时间,动画不会产生平滑的变化。

渐变创建代码

override func draw(_ rect: CGRect) {
        
        let path = CGMutablePath()
        path.addRect(CGRect(x: 0, y: 0, width: 50, height: 50))
        path.move(to: CGPoint(x: 75, y: 75))
        path.addRect(CGRect(x: 75, y: 75, width: 50, height: 50))
        
        let mask = CAShapeLayer()
        mask.path = path
        
        gradient = CAGradientLayer()
        gradient.mask = mask
        gradient.colors = [UIColor.orange.cgColor, UIColor.blue.cgColor]
        gradient.locations = [0, 0]
        gradient.startPoint = CGPoint(x: 0, y: 0)
        gradient.endPoint = CGPoint(x: 1, y: 0)
        gradient.frame = bounds
        
        layer.insertSublayer(gradient, at: 0)
    }

动画代码

func play() {
        let animation = CABasicAnimation(keyPath: "locations")
        animation.fromValue = [0, 0]
        animation.toValue = [1, 1]
        animation.duration = 120
        animation.fillMode = .backwards
        animation.isRemovedOnCompletion = false
        gradient.add(animation, forKey: nil)
    }

代码工作原理演示 https://i.stack.imgur.com/eUvOo.gif

是否有任何选项可以使更改在动画持续时间较长的情况下变得平滑?

这看起来像是 CAGradientLayer

的限制

您可以使用图层 presentation() 查看 CABasicAnimation 的当前进度。

并且使用 CADisplayLink 您可以检查每个帧的表示层具有的更新值,如下所示:

    ...
    gradient.add(animation, forKey: nil)

    let link = CADisplayLink(target: self, selector: #selector(linkHandler))
    link.add(to: .main, forMode: .default)
    link.isPaused = false
}

@objc func linkHandler() {
    NSLog("\(gradient.presentation()?.locations)")
}

从这个测试中你会看到 gest 值更新完全正常,但是 CAGradientLayer 将它们四舍五入到 0.004 左右。想象一下里面有这样的代码

locations = locations.map { round([=11=] * 250) / 250 }

我怀疑你对此无能为力。

你可以做的是用Metal手工绘制渐变。这是一个很大而且有点吓人的话题,但是绘制渐变并不难。查看 Rainbows,它已过时,您必须将语法更新为当前语法,但应该可以

您的 draw 方法每次调用时都会插入一个新的渐变图层。但是这个方法会被重复调用,导致插入多层。在 draw 中你唯一应该做的是 stroking/filling 路径,在这种情况下,你可能根本不应该实现 draw

层的插入不属于draw