Choppy CATextLayer 动画:fontSize + position 同时
Posted
技术标签:
【中文标题】Choppy CATextLayer 动画:fontSize + position 同时【英文标题】:Choppy CATextLayer animation: fontSize + position concurrently 【发布时间】:2017-10-12 03:18:34 【问题描述】:我需要为CATextLayer
的bounds.size.height
、position
和fontSize
制作动画。当我将它们添加到CAAnimationGroup
时,动画期间文本会抖动,就像这样:
https://youtu.be/HfC1ZX-pbyM
在为fontSize
与bounds.size.height
AND/OR position
制作动画时,似乎会出现文本跟踪值的抖动(字符之间的间距)。我已经隔离了fontSize
,它自己运行良好。
如果我同时为边界和字体大小设置动画,如何防止 CATextLayer
中的文本抖动?
编辑
我已经从动画 bounds
继续。现在,我只关心fontSize
+ position
。这里有两个视频展示了不同之处。
仅限fontSize
(平滑):https://youtu.be/FDPPGF_FzLI
fontSize
+ position
(抖动):https://youtu.be/3rFTsp7wBzk
这是代码。
let startFontSize: CGFloat = 16
let endFontSize: CGFloat = 30
let startPosition: CGPoint = CGPoint(x: 40, y: 100)
let endPosition: CGPoint = CGPoint(x: 20, y: 175)
// Initialize the layer
textLayer = CATextLayer()
textLayer.string = "Hello how are you?"
textLayer.font = UIFont.systemFont(ofSize: startFontSize, weight: UIFont.Weight.semibold)
textLayer.fontSize = startFontSize
textLayer.alignmentMode = kCAAlignmentLeft
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.contentsScale = UIScreen.main.scale
textLayer.isWrapped = true
textLayer.backgroundColor = UIColor.lightGray.cgColor
textLayer.anchorPoint = CGPoint(x: 0, y: 0)
textLayer.position = startPosition
textLayer.bounds.size = CGSize(width: 450, height: 50)
view.layer.addSublayer(textLayer)
// Animate
let damping: CGFloat = 20
let mass: CGFloat = 1.2
var animations = [CASpringAnimation]()
let fontSizeAnim = CASpringAnimation(keyPath: "fontSize")
fontSizeAnim.fromValue = startFontSize
fontSizeAnim.toValue = endFontSize
fontSizeAnim.damping = damping
fontSizeAnim.mass = mass
fontSizeAnim.duration = fontSizeAnim.settlingDuration
animations.append(fontSizeAnim)
let positionAnim = CASpringAnimation(keyPath: "position.y")
positionAnim.fromValue = textLayer.position.y
positionAnim.toValue = endPosition.y
positionAnim.damping = damping
positionAnim.mass = mass
positionAnim.duration = positionAnim.settlingDuration
animations.append(positionAnim)
let animGroup = CAAnimationGroup()
animGroup.animations = animations
animGroup.duration = fontSizeAnim.settlingDuration
animGroup.isRemovedOnCompletion = true
animGroup.autoreverses = true
textLayer.add(animGroup, forKey: nil)
我的设备运行的是 ios 11.0。
编辑 2
我逐帧分解了每个动画(仅限fontSize
,以及fontSize
+ position
)。在每个视频中,我一次进步一帧。
在仅限 fontSize
的视频 (https://youtu.be/DZw2pMjDcl8) 中,每一帧都会增加 fontSize
,因此不会出现断断续续的情况。
在fontSize
+ position
视频 (https://youtu.be/_idWte92F38) 中,position
会在每一帧中更新,但不会fontSize
。 fontSize
仅在 60% 的帧中增加,这意味着 fontSize
的动画与 position
不同步,从而导致感知到断续。
所以也许正确的问题是:为什么fontSize
是添加到图层的唯一动画,但在作为CAAnimationGroup
的一部分与position
动画一起添加时却没有在每一帧中进行动画处理?
【问题讨论】:
是否尝试过为文本和背景使用单独的图层?您可以分别为它们设置动画。 “背景”是什么意思?就像在包含层的UIView
包装器中一样,添加到超级视图中?
我指的是区域(例如您视频中的蓝色方块)。如果它是透明的,则不应为边界设置动画。只需将文本层设置为最大可能的大小。
1) 嗯,蓝色方块是CATextLayer
本身的backgroundColor
,所以“文本”和“背景”是同一层的一部分。 bounds
是对的;我已经不再尝试了。 2)我现在的问题是position
。为此,我很难过。我觉得系统字体的跟踪值正在与动画作斗争,因为每个pointSize
都有自己的值,并且在调整大小时必须重新绘制自己。
它看起来更像是持续时间的问题,而不是字体的任何问题。在您也对单个动画应用持续时间的情况下,动画时间线可能会令人困惑。我建议再次尝试删除单个持续时间并仅使用动画组的持续时间。您也可以通过[animGroup.animations valueForKeyPath:@"@max.endTime"]
获得最长持续时间。如果我在这里错了,请纠正我。
【参考方案1】:
Apple DTS 认为这个问题是一个错误。已提交报告。
同时,我将使用CADisplayLink
将CATextLayer.fontSize
的重绘与设备的刷新率同步,这将在每一帧中使用适当的fontSize
重绘图层。
编辑
在修改CADisplayLink
一天左右之后,事实证明绘制到正确的fontSize
很困难,尤其是在与自定义计时功能配对时。所以,我完全放弃CATextLayer
并回到UILabel
。
在 WWDC '17 的 Advanced Animations with UIKit 中,Apple 建议“视图变形”在两个标签状态之间进行动画处理,即两个视图的平移、缩放和不透明度混合。 UIViewPropertyAnimator
为此提供了很大的灵活性,例如混合多个计时功能和擦洗。视图变形对于在 2 个 text
值之间进行转换也很有用,而无需淡出文本表示、更改 text
并淡入。
我确实希望 Apple 可以加强 CATextLayer
对非交互式动画的支持,因为我更喜欢使用一个视图来为相同的文本表示制作动画。
【讨论】:
以上是关于Choppy CATextLayer 动画:fontSize + position 同时的主要内容,如果未能解决你的问题,请参考以下文章
Firefox 中的 jQuery 和 CSS 动画 Choppy
新“页面加载”时 Firefox 中的 Choppy 和 Jerky CSS3 动画