如何将插入符形状动画化为平滑的弧线?

How do I animate a caret shape into a smooth arc?

我想创建一个动画,其中插入符号形状 (">") 以动画形式变为圆弧。它适用于一个应用程序,其中有一个插入符转换为 3/4 圆弧,然后在网络事务运行时旋转。

动画应该是这样的:

每当我尝试做这样的路径动画时,我都会得到非常奇怪的结果。如何制作流畅的动画?

这种动画的秘诀是将路径安装到 CAShapeLayer 中,并在路径上使用 CABasicAnimation 将路径从起始状态转换为结束状态。棘手的部分是开始和结束路径需要具有相同数量和类型的控制点。

我写了一个演示应用程序,它创建了上面的动画。您可以 download it from Github (link)

这里是 repo 的自述文件,它详细解释了它是如何工作的:


CaretToArcAnimation

此项目将插入符号简单转换为跨越 3/4 圆的弧形。它看起来像这样:

它通过动画安装到 CAShapeLayer 中的路径来工作。

为了使路径动画正常工作,起始路径和结束路径需要具有相同数量和类型的控制点。

为了从插入符到圆弧设置动画,它将插入符创建为 2 条三次贝塞尔曲线。第一条“曲线”(实际上是一条直线)从插入符号的左下角开始,沿着下方线段经过 1/3 和 2/3 的点,并在“弯曲”处结束插入符。这将创建呈现为线段的贝塞尔曲线。第二条曲线也是直线贝塞尔曲线。第二个从插入符号的弯曲处开始,到左上角结束。

圆弧也由 2 条三次贝塞尔曲线组成,绘制方向与插入符号中的曲线相同。但是,选择圆弧的贝塞尔曲线的控制点,以便生成的曲线非常接近 3π/2 弧度或 270 度或 3/4 圆弧的圆弧。圆弧比它替换的插入符号稍大,并且朝向相同。

如果添加贝塞尔曲线控制点的可视化表示会更容易理解,如下所示:

落在 caret/curve 上的红点是 2 条贝塞尔曲线的端点。圆弧外侧的红点是定义曲线形状的控制点。对于插入符号形状,控制点在线上,这导致贝塞尔曲线呈直线形状。

我使用 this article 来获取两条贝塞尔曲线的控制点。我不想弄清楚数学,所以我只是在那个页面的交互式弧形渲染器上设置曲线来绘制 3/8 的圆,记下所有控制点的坐标,然后翻转它们得到第二个贝塞尔曲线。

这是我用来查找贝塞尔曲线控制点的贝塞尔曲线逼近器网络模拟的屏幕截图:

(在该屏幕截图中,角度滑块以弧度表示。3/4 圆弧的一半是一个完整圆的 3/8,或 2π 的 3/8。2π * 3/8 大约是2.36,所以这是我选择的弧角。)

CaretToArcAnimation 应用程序定义了 CaretToArcView class,它是 UIView.

的子class

大多数有趣的工作都是在 CaretToArcView class 中完成的。

它有一个静态变量 layerClass,其中 returns CAShapeLayer.self。这会导致视图的支持层被设置为 CAShapeLayer。

class override var layerClass: AnyClass {
        return CAShapeLayer.self
    }

CaretToArcView.swift 文件定义了一个枚举 ViewState:

enum ViewState: Int {
    case none
    case caret
    case arc
}

自定义视图 class 具有 ViewState 类型的变量 viewState。它的初始值为.none,表示视图中没有安装路径。

如果将状态设置为.caret.arc,它会检查当前图层路径是否为nil。如果是,它会将适当的路径安装到没有动画的图层中。

如果以前的路径是其他路径类型,它会构建一个具有新形状的路径,并创建一个 CABasicAnimationfromState 的以前的路径,以及 toState的新路径。动画的持续时间使用实例变量 animationDuration 设置。动画使用缓入缓出时间,但很容易更改。

class还有public函数rotate(_ doRotate: Bool)。如果你用 doRotate == false 调用它,它会从视图层中删除所有动画。如果您使用 doRotate == true 调用它,它会向层添加一个无限重复的旋转动画,使用线性计时将其旋转 360°,一遍又一遍。

该应用的视图控制器驱动 CaretToArcView 中的设置,并且它具有防止自定义视图同时调用两个动画的逻辑(如果您这样做会发生奇怪的事情。不要.)

应用程序的屏幕如下图所示。

视图控制器在切换动画期间禁用按钮和开关,并在旋转开关打开且形状正在旋转时禁用“切换”按钮。