持续时间长的平滑动画 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
。
创建了具有鲜明颜色过渡的 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
。