iOS:CAShapeLayer 路径转换

2023-12-06

我用过CAShape Layer在对路径进行基本变换之前 - 从较小的圆圈到较大的圆圈。足够好了,但后来我尝试将三角形变成圆形;它有效,但转变很奇怪。换句话说,从一种形状到另一种形状,在形成最终形状之前,它会“翻转”、“扭曲”。对于相同的形状,没有问题——从一个圆到一个圆——但是对于不同的形状,转换很奇怪。

我想知道这是否是预期的方式?或者是否有其他技巧或解决方法,我们可以使用以下方法将一个形状平滑地按比例转换为另一种不同的形状CAShape Layer(或者是否有其他方法可以做到这一点;不一定是CAShape Layer)?提前致谢。


通过仔细选择控制点等,您可能可以绘制一条绘制为三角形的路径,但其线段和控制点的数量与您要绘制的圆形相同。像这样(所有数值均假设 iPhone 屏幕,但要点是通用的):

@implementation ViewController
{
    CAShapeLayer* shapeLayer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    shapeLayer = [[CAShapeLayer alloc] init];

    CGRect bounds = self.view.bounds;
    bounds.origin.x += 0.25 * bounds.size.width;
    bounds.size.width *= 0.5;
    bounds.origin.y += 0.25 * bounds.size.height;
    bounds.size.height *= 0.5;

    shapeLayer.frame = bounds;
    shapeLayer.backgroundColor = [[UIColor redColor] CGColor];

    [self.view.layer addSublayer: shapeLayer];
    [self toCircle: nil];
}

CGPoint AveragePoints(CGPoint a, CGPoint b)
{
    return CGPointMake((a.x + b.x) * 0.5f, (a.y + b.y) * 0.5f);
}

- (IBAction)toCircle:(id)sender
{
    UIBezierPath* p = [[UIBezierPath alloc] init];

    [p moveToPoint: CGPointMake(80, 56)];
    [p addCurveToPoint:CGPointMake(144, 120) controlPoint1:CGPointMake(115.34622, 56) controlPoint2:CGPointMake(144, 84.653778)];
    [p addCurveToPoint:CGPointMake(135.42563, 152) controlPoint1:CGPointMake(144, 131.23434) controlPoint2:CGPointMake(141.0428, 142.27077)];
    [p addCurveToPoint:CGPointMake(48, 175.42563) controlPoint1:CGPointMake(117.75252, 182.61073) controlPoint2:CGPointMake(78.610725, 193.09874)];
    [p addCurveToPoint:CGPointMake(24.574375, 152) controlPoint1:CGPointMake(38.270771, 169.80846) controlPoint2:CGPointMake(30.191547, 161.72923)];
    [p addCurveToPoint:CGPointMake(47.999996, 64.574379) controlPoint1:CGPointMake(6.9012618, 121.38927) controlPoint2:CGPointMake(17.389269, 82.24749)];
    [p addCurveToPoint:CGPointMake(80, 56) controlPoint1:CGPointMake(57.729225, 58.957207) controlPoint2:CGPointMake(68.765656, 56)];
    [p closePath];

    [CATransaction begin];
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.duration = 3.f;
    pathAnimation.fromValue = (id)shapeLayer.path;
    pathAnimation.toValue = (id)p.CGPath;
    [shapeLayer addAnimation:pathAnimation forKey:@"path"];
    [CATransaction setCompletionBlock:^{
        shapeLayer.path = p.CGPath;
    }];
    [CATransaction commit];

    double delayInSeconds = 4.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self toTriangle: nil];
    });

}

- (IBAction)toTriangle: (id)sender
{
    UIBezierPath* p = [[UIBezierPath alloc] init];

    // Triangle using the same number and kind of points...
    [p moveToPoint: CGPointMake(80, 56)];
    [p addCurveToPoint: AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152))  controlPoint1:CGPointMake(80, 56) controlPoint2:AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152))];
    [p addCurveToPoint:CGPointMake(135.42563, 152) controlPoint1:AveragePoints(CGPointMake(80, 56), CGPointMake(135.42563, 152)) controlPoint2:CGPointMake(135.42563, 152)];
    [p addCurveToPoint:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152)) controlPoint1:CGPointMake(135.42563, 152) controlPoint2:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152))];
    [p addCurveToPoint:CGPointMake(24.574375, 152) controlPoint1:AveragePoints(CGPointMake(135.42563, 152), CGPointMake(24.574375, 152)) controlPoint2:CGPointMake(24.574375, 152)];
    [p addCurveToPoint: AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) controlPoint1:CGPointMake(24.574375, 152) controlPoint2:AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) ];
    [p addCurveToPoint:CGPointMake(80, 56) controlPoint1:AveragePoints(CGPointMake(24.574375, 152),CGPointMake(80, 56)) controlPoint2:CGPointMake(80, 56)];
    [p closePath];

    [CATransaction begin];
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.duration = 3.f;
    pathAnimation.fromValue = (id)shapeLayer.path;
    pathAnimation.toValue = (id)p.CGPath;
    [shapeLayer addAnimation:pathAnimation forKey:@"path"];
    [CATransaction setCompletionBlock:^{
        shapeLayer.path = p.CGPath;
    }];
    [CATransaction commit];

    double delayInSeconds = 4.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self toCircle: nil];
    });

}

Even with段数和控制点的数量相同,动画still看起来有点奇怪,可能不是你想要的。原因是 CA 似乎将所有线段和控制点一对一匹配,然后对所有点在它们之间进行线性插值。由于控制点和生成的路径之间的关系不是线性的(在本例中为三次),因此线性插值控制点位置不会导致路径以线性方式移动。如果运行此代码,您可以看到,当它转换时,三角形的一侧有奇怪的驼峰,其中圆形路径在圆弧中具有其他点。

更一般地说,期望 CA 以某种特定的方式在两条任意路径之间神奇地变形(而事实证明这种方式在外观上是理想的)是不合理的。即使努力手工构建这些路径,以便它们可以祈祷变形,它们仍然不像我想象的那样。

通过使用扁平路径(即由许多小直线而不是弯曲路径元素组成的路径)来实现所需的效果可能更合理。即使这看起来也很重要,因为您再次需要两条路径具有相同数量的线段,并且您必须构造这些线段,以便公共点是整个路径上正确数量的线段小路。

总而言之:这是一个相当复杂的问题,CoreAnimation 提供的简单/免费的解决方案不太可能满足您的需求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS:CAShapeLayer 路径转换 的相关文章

随机推荐

  • 如何使用 NAs - R 简单地计算行数[重复]

    这个问题在这里已经有答案了 我正在尝试计算整个 df 的 NA 行数 因为我正在计算 NA 的行数占 df 总行数的百分比 我已经看过这个帖子了 确定具有 NA 的行数但它只显示特定范围的列 tl dr 逐行 你会想要sum complet
  • fullcalendar:有没有办法仅在我通过事件函数加载事件后调用 dayRender

    我正在构建的网络应用程序中使用 fullcalendar 我加载我的事件事件函数和阿贾克斯 这是我的代码 var ajaxData var eventsJsonArray var json backgrundColor var json i
  • “实验性”gradle 插件在本机库链接期间出现“未定义引用”错误

    我尝试使用基于 实验性 com android 的新构建系统创建一个简单的 JNI 应用程序 model application com android tools build gradle experimental 0 9 2 gradl
  • 从 StreamReader 读取文件时出现 C# 编码问题(问号)

    我在从 Windows Phone 应用程序读取 txt 文件时遇到问题 我制作了一个简单的应用程序 它从 txt 文件读取流并打印它 不幸的是 我来自意大利 我们有很多带有口音的信件 问题是 事实上所有带重音的字母都被打印为问号 这是示例
  • 如何通过 XAML 设置 LISTBOX 工具提示

    我在 WPF 中有一个列表框 如下所示
  • 如何动态迭代/检测 ValueTuple 中具有空成员的成员类型? [复制]

    这个问题在这里已经有答案了 我正在尝试利用ValueTuple简洁地输入一个列表N泛型方法的参数列表中的类型 然后迭代该类型列表 但是 我在迭代类型时遇到了问题 因为初始Tuple has null会员 所以打电话 GetType 给了我一
  • JScrollPane 未滚动超过设定大小

    我正在使用一个滚动窗格 里面有一个 JPanel 它绘制一个正方形网格 这些正方形是来自 数组的对象 如果数组是大小为 18 2 的矩形 83 81 则它看起来像 https i stack imgur com MEBrt png 注意网格
  • 如何使用 python 和底图绘制不规则间隔的 RGB 图像?

    鉴于我有三个矩阵来描述我想要绘制的数据 lons 具有 n lons n lats 的二维矩阵 lats 具有 n lons n lats 的二维矩阵 dataRGB 具有 n lons n lats 3 的 3D 矩阵 使用 python
  • 从 Linux 内核发送小 UDP 数据包到 LOOPBACK

    情况 我的代码基本上被侵入了 Linux 内核的驱动程序 我想在将值得注意的原始事件发送到主系统之前通知用户空间中的应用程序 解决方案步骤 我在这里找到了一个从内核空间发送 UDP 数据包的好例子 http kernelnewbies or
  • 当我使用 pyinstaller 创建 .exe 文件时,“应用程序冻结时找不到 dynlib/dll”,因为 pylibmtx 发生了错误

    when I 我正在努力制作 exe 文件 错误消息显示第 9 行有问题 所以我检查了我的代码 有导入的库 pylibdmtx 用于解码数据矩阵 所以我尝试删除有关该库的所有内容以及其他相关属性 然后就可以了 我试图找出该问题以进行故障排除
  • OpenCL - 多个 GPU 缓冲区同步

    我有一个 OpenCL 内核 用于计算系统中其他粒子对某个粒子施加的总力 然后是另一个积分粒子位置 速度的内核 我想在多个 GPU 上并行化这些内核 基本上为每个 GPU 分配一定数量的粒子 但是 我必须多次运行该内核 并且每个 GPU 的
  • Elixir 列表解释为 char 列表

    我刚刚开始使用 Elixir 我正在使用 ExUnit 为我自己实现的简单 Enumerable 函数编写一些测试 而不使用标准 Enum 模块 在我的测试中 我发现每当我引用该列表时 7 8 9 一旦在标准输出中打印出来 我就会看到字符列
  • 启用/安装 GD 扩展? --没有-gd

    当我的 phpinfo 在 配置命令 中输出时 如何启用 或者可能我需要安装 GD 没有 gd 我的 phpinfo 输出 Core 中也没有列出 gd AWS 上的 PHP 版本 5 2 4 如果您使用的是基于 Debian 的服务器 例
  • winforms 数据绑定仅适用于开发机器

    我的目标是框架 4 0 这在开发计算机上运行良好 我可以在启动时看到表单 其中文本框显示绑定消息 但是 当我在任何其他计算机上部署可执行文件时 它不会声称 无法绑定到数据源上的属性或列注释 非常奇怪的是 如果我针对 3 5 框架进行编译 它
  • scanf 不读取输入

    我阅读了更多有关 scanf 的帖子 发现一些答案机器人没有帮助我 while comanda int tmp if scanf d tmp 0 getchar else comanda tmp fprintf stdout d coman
  • 中断处理程序可以被抢占吗?

    我知道Linux有嵌套中断 其中一个中断可以 抢占 另一个中断 但是其他任务又如何呢 我只是想了解linux如何处理中断 它们可以被其他用户任务 内核任务抢占吗 Reading 为什么在中断上下文中执行的内核代码 线程无法休眠 链接到罗伯特
  • 将图像从一个工作簿复制到另一工作簿

    我在合并单元格范围 S1 V8 的sheet1 中有一张图像 我不知道这张图片的名称 因为每次从模板创建新的 Excel 文件时 我们都会在该区域粘贴不同的图片 我想将此工作簿或另一个工作簿中此范围内的图片复制到单元格 A6 中名为 数据库
  • PHP:在 6 行后结束并开始新的

    我想做一个表 然后每6行应该有一个tr 然后行在td里面 所以例子 tr td td 1 td td 2 td td 3 td td 4 td td 5 td td 6 tr tr start new tr after 6 rows rep
  • Powershell - 我的倒计时器出了什么问题

    我有一个计数器脚本需要两个参数 1 计数器启动前等待的秒数 2 计数器持续时间 以秒为单位 例如 如果我输入 3 10 我希望 3 秒后计时器将从 10 倒计时到 0 并每秒将其写入输出 这是我的脚本 timeBeforeStart arg
  • iOS:CAShapeLayer 路径转换

    我用过CAShape Layer在对路径进行基本变换之前 从较小的圆圈到较大的圆圈 足够好了 但后来我尝试将三角形变成圆形 它有效 但转变很奇怪 换句话说 从一种形状到另一种形状 在形成最终形状之前 它会 翻转 扭曲 对于相同的形状 没有问