Choppy CATextLayer 动画:fontSize + position 同时

Posted

技术标签:

【中文标题】Choppy CATextLayer 动画:fontSize + position 同时【英文标题】:Choppy CATextLayer animation: fontSize + position concurrently 【发布时间】:2017-10-12 03:18:34 【问题描述】:

我需要为CATextLayerbounds.size.heightpositionfontSize 制作动画。当我将它们添加到CAAnimationGroup 时,动画期间文本会抖动,就像这样:

https://youtu.be/HfC1ZX-pbyM

在为fontSizebounds.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 会在每一帧中更新,但不会fontSizefontSize 仅在 60% 的帧中增加,这意味着 fontSize 的动画与 position 不同步,从而导致感知到断续。

所以也许正确的问题是:为什么fontSize 是添加到图层的唯一动画,但在作为CAAnimationGroup 的一部分与position 动画一起添加时却没有在每一帧中进行动画处理?

【问题讨论】:

是否尝试过为文本和背景使用单独的图层?您可以分别为它们设置动画。 “背景”是什么意思?就像在包含层的 UIView 包装器中一样,添加到超级视图中? 我指的是区域(例如您视频中的蓝色方块)。如果它是透明的,则不应为边界设置动画。只需将文本层设置为最大可能的大小。 1) 嗯,蓝色方块是CATextLayer 本身的backgroundColor,所以“文本”和“背景”是同一层的一部分。 bounds 是对的;我已经不再尝试了。 2)我现在的问题是position。为此,我很难过。我觉得系统字体的跟踪值正在与动画作斗争,因为每个pointSize 都有自己的值,并且在调整大小时必须重新绘制自己。 它看起来更像是持续时间的问题,而不是字体的任何问题。在您也对单个动画应用持续时间的情况下,动画时间线可能会令人困惑。我建议再次尝试删除单个持续时间并仅使用动画组的持续时间。您也可以通过[animGroup.animations valueForKeyPath:@"@max.endTime"] 获得最长持续时间。如果我在这里错了,请纠正我。 【参考方案1】:

Apple DTS 认为这个问题是一个错误。已提交报告。

同时,我将使用CADisplayLinkCATextLayer.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 动画

Choppy Animation SwiftUI 嵌套视图

CATextLayer 的动画截断

CATextLayer 的动画foregroundColor 属性

自动布局和 CATextLayer iOS7