十七.核心动画 - 使用重复图层(CAReplicatorLayer)构建自定义loading视图.

news2024/11/26 3:46:49

引言

本篇博客我们来详细的讨论一下CAReplicatorLayer图层,它是一个功能非常强大的工具,允许我们通过重复图层来创建复杂且高效的动画效果,无论是加载动画,粒子效果,还是其它重复性的图形动画,CAReplicatorLayer都能轻松实现。

接下里,我们就来深入的探讨一下如何使用CAReplicatorLayer来构建自定义的loading动画。我们将从基础概念入手,逐步构建一个简单的loading动画,并探讨一些优化和扩展的方法。希望通过这篇文章,你能对CAReplicatorLayer有更深的理解,并能够应用它来打造出色的动画效果。

基础

CAReplicatorLayer的作用是可以高效的生成许多相似的图层。它会绘制一个或多个图层的子图层,并且在每个复制体上应用不同的变换。

图层属性

CAReplicatorLayer有几个重要的属性,每个属性都可以用来定制复制图层的效果和动画:

  1. instanceCount:最常用的属性,它指定了创建的副本的数量,默认值为1,也就是不创建副本。
  2. instanceDelay:该属性指的是每个副本相对于前一个副本的延迟时间,比如当我们做动画时,CAReplicatorLayer产生的副本并不会同时进行动画,而是有一定延迟(所以很适合做loading)。
  3. instanceTransform:该属性是一个CATransform3D类型的值,用于指定每个副本相对于前一个副本做的变换(记住是相对前一个副本奥)。
  4. instanceColor:该属性指的是副本图层的颜色,如果副本的颜色需要与原始图层不同,则需要设置该属性。
  5. instanceRedOffset:该属性指定了副本图层的红色通道偏移量,用于改变副本的颜色。
  6. instanceGreenOffset:指定副本图层的绿色通道偏移。
  7. instanceBlueOffset:指定副本图层的蓝色通道偏移量。
  8. instanceAlphaOffset:指定副本图层的透明度偏移量。
  9. preservesDepth:该属性指定是否保留图层的深度信息,默认值是NO,如果设置为YES,则副本会保留3D变换的深度信息。

图层使用

我们接下来通过一个简单的示例来展示CAReplicatorLayer的基础用法。

1.创建CAReplicatorLayer,并添加到视图的图层中:
    /// 重复图层
    let replicatorLayer = CAReplicatorLayer()
    replicatorLayer.frame = self.bounds
    self.layer.addSublayer(replicatorLayer)

我们创建了一个复制图层,设置其大小等于视图的大小,并将它添加到了视图的图层当中。

2.添加子图层

接下来我们需要创建一个子图层,然后它才会被CAReplicatorLayer复制,为了展示效果,我们就来创建一个简单的小圆点。

    /// 圆点
    let dotLayer = CALayer()
    dotLayer.frame = CGRect(x: self.bounds.width / 2 - 5, y: 0, width: 10, height: 10)
    dotLayer.backgroundColor = UIColor.red.cgColor
    dotLayer.cornerRadius = 5
    replicatorLayer.addSublayer(dotLayer)

这段代码我们,创建了一个圆形红色的小点,并将其添加到了CAReplicatorLayer中。

我们先创建出来看一下效果:

let loadingView = PHLoadingView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
self.view.addSubview(loadingView)
loadingView.center = self.view.center

我们创建了一个大小为40*40屏幕居中的视图,效果如下:

只有一个小红点,没有什么特别的地方,这是因为我们只是将图层添加到了复制图层上,但是并没有设置CAReplicatorLayer的任何属性,接下来我们就来开始体验每个属性给视图带来的变化。

3.配置CAReplicatorLayer的属性

首先我们来配置两个最基础的属性,instanceCount和instanceTransform,指定复制图层的个数并且让每个复制图层相对前一个图层进行变化,这样我们才能观察到每个复制图层:

        replicatorLayer.instanceCount = 10
        var transform = CATransform3DIdentity
        transform = CATransform3DRotate(transform, CGFloat.pi*2/10, 0, 0, 1)
        replicatorLayer.instanceTransform = transform

上面的代码中我们设置了复制图层的个数为10,并且每个图层进行CGFloat.pi*2/10弧度的偏移,那么10个刚好围成一圈,效果如下:

4.修改颜色

通常instanceColor属性需要和子图层的颜色相结合,然后再配合instanceRedOffsetinstanceGreenOffsetinstanceBlueOffset 和 instanceAlphaOffset四个属性共同作用来调整每个副本的颜色:

        replicatorLayer.instanceColor = UIColor.yellow.cgColor
        replicatorLayer.instanceRedOffset = -0.1
        replicatorLayer.instanceGreenOffset = -0.1
        replicatorLayer.instanceBlueOffset = -0.1

效果如下:

5.修改复制图层的动画延迟

instanceDelay属性的需要结合动画才能看出来,那我们为图层添加一个由小变大的动画,动画持续时间为1秒,并设置图层的instanceDelay为0.1,代码如下:

        let animation = CABasicAnimation(keyPath: "transform.scale")
        animation.fromValue = 1
        animation.toValue = 0.1
        animation.duration = 1
        animation.repeatCount = Float.greatestFiniteMagnitude
        dotLayer.add(animation, forKey: nil)

我们来看一下效果:

由于延迟的存在使得每个复制图层轮流缩小,显得像是动起来了,这样我们最基础的加载动画就已经实现完成了。

进阶loading动画

上面我们非常轻松的就实现了一个简单的自定义loading动画,接下来我们使用CAReplicatorLayer继续实现一些常见的loading动画。

视频上传框

视频和图片上传的功能应该比较常见,为了让上传的动画更有趣味,有些设计团队会将loading显示在正在上传的边框上,效果如下:

整个动画的核心仍然是使用CAReplicatorLayer复制图层动画延迟的属性,下面我们来看一下代码,

1.创建图片,以及重复图层:
    /// 图片
    private let imageView = UIImageView()
    /// 复制图层
    private let replicatorLayer = CAReplicatorLayer()

        imageView.frame = self.bounds
        imageView.image = UIImage(named: "coreAnimation")
        imageView.layer.masksToBounds = true
        imageView.layer.cornerRadius = 10.0
        self.addSubview(imageView)
        
        self.layer.addSublayer(replicatorLayer)
        replicatorLayer.instanceCount = 10
        replicatorLayer.instanceDelay = 0.1
        replicatorLayer.instanceAlphaOffset = -0.1
        replicatorLayer.frame = self.bounds

图片的大小和重复图层的大小都与父视图大小相同。并设置了重复图层的个数以及执行动画的时间延迟,和每个图层的透明度偏移量。

2.创建子图层添加到复制图层:
    /// 圆点
    private let dotLayer = CALayer()
        dotLayer.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
        let image = UIImage(named: "xingxing")
        dotLayer.contents = image?.cgImage

我们设置子图层的大小为10*10,并设置了寄宿图。

3.创建一个曲线,并让子图层沿着曲线做关键帧动画:
        bezierPath = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: self.bounds.size.width, height: self.bounds.size.height), cornerRadius: 0.0)
       
        let pathAnimation = CAKeyframeAnimation(keyPath: "position")
        pathAnimation.path = bezierPath.cgPath
        pathAnimation.duration = 4.0
        pathAnimation.repeatCount = Float.greatestFiniteMagnitude
        dotLayer.add(pathAnimation, forKey: nil)

由于图层的延迟执行动画功能,就会出现类似加载的效果了。

绘制Logo动画

通常还有一些常见我们会绘制自己的LOGO当做动画,比如下面的效果:

1.绘制线

想要实现外面的小框,首先我们需要使用CGPath绘制出它的轮廓,然后使用CAShapeLayer图层进行填充,再为图层添加一个动画代码如下:

    func addLineAnimation()  {
        let lineLayer = CAShapeLayer()
        lineLayer.strokeColor = UIColor.red.cgColor
        lineLayer.fillColor = UIColor.clear.cgColor
        lineLayer.lineWidth = 2.0
        self.layer.addSublayer(lineLayer)
        
        let width = self.bounds.size.width
        let height = self.bounds.size.height
        // 创建一个路径
        let path = UIBezierPath()
        path.move(to: CGPoint(x: width/4.0, y: 0.0))
        path.addLine(to: CGPoint(x: width/2.0, y: 10.0))
        path.addLine(to: CGPoint(x: 0.0, y: 10.0))
        path.addLine(to: CGPoint(x: 0.0, y: height))
        path.addLine(to: CGPoint(x: width, y: height))
        path.addLine(to: CGPoint(x: width, y: 10.0))
        path.addLine(to: CGPoint(x: width/2.0, y: 10.0))
        path.addLine(to: CGPoint(x: width*3/4.0, y: 0.0))
        lineLayer.path = path.cgPath
        
        let animaionGroup = CAAnimationGroup()
        animaionGroup.duration = 3
        animaionGroup.repeatCount = Float.greatestFiniteMagnitude
        animaionGroup.isRemovedOnCompletion = false
        animaionGroup.fillMode = .forwards
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1.0
        animation.duration = 2
        animaionGroup.animations = [animation]
        lineLayer.add(animaionGroup, forKey: nil)
    }
2.绘制点

使用普通的CALayer图层绘制点,并将其添加到CAReplicatorLayer图层,然后设置复制图层的属性,并为CALayer添加透明度和缩放的动画效果,代码如下:

    func addDotAnimation() {
        let width = self.bounds.size.width
        // 复制图层
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame = self.bounds
        replicatorLayer.instanceCount = 3
        replicatorLayer.instanceDelay = 0.3
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(width/4.0, 0, 0)
        self.layer.addSublayer(replicatorLayer)
        
        let dotLayer = CALayer()
        dotLayer.frame = CGRect(x: 7, y: 20, width: 8, height: 8)
        dotLayer.backgroundColor = UIColor.red.cgColor
        dotLayer.cornerRadius = 4
        replicatorLayer.addSublayer(dotLayer)
        
        let animation = CABasicAnimation(keyPath: "transform.scale")
        animation.fromValue = 1
        animation.toValue = 0.1
        animation.duration = 1
        animation.repeatCount = Float.greatestFiniteMagnitude
        
        let opacityAnimation = CABasicAnimation(keyPath: "opacity")
        opacityAnimation.fromValue = 1
        opacityAnimation.toValue = 0.1
        opacityAnimation.duration = 1
        opacityAnimation.repeatCount = Float.greatestFiniteMagnitude
        
        let group = CAAnimationGroup()
        group.animations = [animation,opacityAnimation]
        group.beginTime = 2
        group.duration = 1
        group.repeatCount = Float.greatestFiniteMagnitude
        dotLayer.add(group, forKey: nil)

    }

绘制文字动画

文字想要获取路径并不容易,但是我们可以借助CAReplicatorLayer实现另外一种闪动效果的文字load效果(在真机上效果会更好一些):

1.创建重复图层
    /// 复制图层
    private let replicatorLayer = CAReplicatorLayer()
    self.layer.addSublayer(replicatorLayer)
    replicatorLayer.frame = self.bounds
    replicatorLayer.instanceCount = 10
    replicatorLayer.instanceDelay = 0.1
    replicatorLayer.instanceAlphaOffset = -0.1

仍然还是先创建了一个重复图层,并且设置了它的大小与父视图大小相同,设置它的重复个数为10动画延迟为0.1秒,另外我们调整了它的透明度偏移量,这就意味着每一个图层会比前面的图层更透明一些。

2.创建背景图层
    /// 普通图层
    private let backLayer = CALayer()

        backLayer.frame = CGRect(x: 0, y: 0, width: self.bounds.size.width / 10.0, height: self.bounds.size.height)
        backLayer.backgroundColor = UIColor.green.cgColor
        replicatorLayer.addSublayer(backLayer)

创建了一个绿色背景图层,它的宽度为视图宽度的1/10。

3.创建文字图层
    /// textLayer
    private let textLayer = CATextLayer()
        textLayer.frame = self.bounds
        textLayer.string = "Loading..."
        textLayer.fontSize = 30
        textLayer.font = UIFont.boldSystemFont(ofSize: 30.0)
        textLayer.foregroundColor = UIColor.red.cgColor
        textLayer.alignmentMode = .center
        textLayer.position = CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2)
        self.layer.mask = textLayer

这里我们把文字图层设置为了父图层的蒙版属性。这就意味着只有textLayer不透明的部分才会被渲染出来。

4.添加动画
        //从左到右
        let animation = CABasicAnimation(keyPath: "position.x")
        animation.fromValue = 0
        animation.toValue = self.bounds.size.width
        animation.duration = 2
        animation.repeatCount = Float.greatestFiniteMagnitude
        backLayer.add(animation, forKey: nil)

为普通的背景图层添加动画,从左向右移动,这样就可以出现上面的效果咯。

结语

通过本文,我们深入了解了 CAReplicatorLayer 的基本用法和一些进阶技巧。从创建简单的加载动画到实现围绕方框移动、绘制 logo 和闪烁文字等复杂效果,CAReplicatorLayer 展现了其强大的功能和灵活性。

CAReplicatorLayer 不仅能够大幅提高动画的性能,还能让开发者用简洁的代码实现复杂的视觉效果。希望本文能为大家提供一些灵感,鼓励大家在实际开发中多多尝试,利用 CAReplicatorLayer 创造出更多精彩的动画效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1985864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

init.rc及其一些语法

1,rc文件 on property:同时判断两个条件 拷打gbt on property:sys.usb.configncm,adb 这是什么意思 config是ncm或adb 这是一个Android系统的属性,它表示USB接口的配置。"ncm"代表使用NCM(Network Control Model)协议…

11、Fawkes

难度 高 目标 2个root 3个flag 当我看到这种类型我就知道肯定是docker系列的连锁环境了 netdiscover -i eth0 -r 192.168.189.0/24 kali 192.168.189.58 靶机 192.168.189.69 扫描一下开放的端口和服务识别 应该是存在ftp 的匿名访问,然后还有一个monkeycom&…

【Material-UI】Autocomplete 组件中的自定义过滤功能(Custom filter)详解

文章目录 一、简介二、createFilterOptions 工厂函数配置选项返回值示例代码代码解释 三、高级自定义:模糊匹配四、实际应用场景1. 数据清理和标准化2. 特定业务逻辑 五、总结 Material-UI 的 Autocomplete 组件不仅提供了强大的自动完成功能,还允许开发…

Prometheus监控组件在SpringBoot项目中使用实践

Prometheus监控组件在SpringBoot项目中使用实践 时间:2024/7/29 背景:本人最近参与的一个项目,要监控远程软硬件以及本地软硬件,实现远程监控以及告警功能。 开发环境: JDK1.8,Maven,PostgreS…

automa自动化工作流教程(三)循环元素进行操作

循环元素 选择器选中的必须是多个元素,如果是css选择器,举例:class属性有多个 .postTitle 并且要生成或填上循环id 有开发需求 call me

【轨物推荐】谈谈科学、工程与技术的关系

原创 宋华振 说东道西 2022年07月05日 23:34 上海 为什么要谈这个话题? 前几天,戴老师转发了一篇《中国科学创新四十年-可能还没搞明白科学和技术的基本概念》,这篇文章谈及了科学与技术的差异,其中谈到了几个误区,误…

HCIP实验-MGRE+OSPF

实验拓扑图: 实验要求: 1.R6为ISP,只能配置IP地址,R1-R5的环回为私有网段 2.R1/4/5为全连的MGRE结构, R1/2/3为星型的拓扑结构,R1为中心站点 3.所有私有网段可以互相通讯,私有网段使用ospf协…

【C++】————哈希表

作者主页: 作者主页 本篇博客专栏:C 创作时间 :2024年8月6日 前言: 在计算机科学的广袤世界中,数据结构犹如基石,支撑着各种高效算法的构建与运行。而哈希表(Hash Table)&#…

【Python系列】使用 `isinstance()` 替代 `type()` 函数

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

24. 两两交换链表中的节点(Java)

目录 题目描述:示例 :代码实现: 题目描述: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换&am…

基于飞腾E2000的科东软件Intewell工业实时操作系统方案

科东软件Intewell工业实时操作系统是完全自主开发的产品,具有20年以上行业应用经验。Intewell基于微内核架构设计,具备高实时、高可靠等特点,同时虚拟化技术支持GPOSRTOS等多种操作系统架构,可实现实时和非实时业务融合应用&#…

揭秘LoRA:利用深度学习原理在Stable Diffusion中打造完美图像生成的秘密武器

文章目录 引言LoRA的原理LoRA在角色生成中的应用LoRA在风格生成中的应用LoRA在概念生成中的应用LoRA在服装生成中的应用LoRA在物体生成中的应用结论 引言 在生成式人工智能领域,图像生成模型如Stable Diffusion凭借其出色的生成效果和广泛的应用场景,逐…

渲染技术如何应对数据增长与计算挑战

随着科技的飞速发展,数字内容的制作与呈现变得日益复杂和精细,这对渲染技术提出了前所未有的挑战。特别是在数据爆炸式增长和计算需求急剧提升的背景下,如何优化渲染技术,以应对这些挑战,成为了一个亟待解决的问题。 …

牛客JS题(二十三)判断质数

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 原型链如何优雅的判断质数 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charsetutf-8></head><body><script type"text/javascript">/*** 素…

试用期没烦恼 神器!!!使用doxygen+Graphviz自动生成函数调用关系图

大家好&#xff0c;我是 小杰学长 使用doxygenGraphviz自动生成函数调用关系图 gitee源码仓库链接跳转 前言 1.下载 首先&#xff0c;下载2个软件&#xff0c; 最新版即可&#xff0c; Doxygen 下载地址&#xff1a; https://sourceforge.net/projects/doxygen/ 下载步骤…

OLAP技术与数据仓库:深度分析与决策支持

目录 一、OLAP 概述 二、OLAP应用场景 三、OLAP对数据仓库的意义 一、OLAP 概述 OLAP&#xff08;Online Analytical Processing&#xff09;即联机分析处理&#xff0c;是一种用于多维数据分析的技术和工具。它允许用户通过多维数据模型进行复杂的分析&#xff0c;以便快速浏览…

sqli-labs(超详解)——Lass32~Lass38

Lass32&#xff08;宽字节注入&#xff09; 源码 function check_addslashes($string) {$string preg_replace(/. preg_quote(\\) ./, "\\\\\\", $string); //escape any backslash$string preg_replace(/\/i, \\\, $string); …

【iOS】——AutoReleasePool底层原理及总结

自动释放池 AutoreleasePool自动释放池用来延迟对象的释放时机&#xff0c;将对象加入到自动释放池后这个对象不会立即释放&#xff0c;等到自动释放池被销毁后才将里边的对象释放。 自动释放池的生命周期 从程序启动到加载完成&#xff0c;主线程对应的runloop会处于休眠状…

C++初学(11)

不知不觉就第11篇了QWQ 11.1、指针和自由存储空间 之前提到了计算机程序在存储数据时必须跟踪的3个基本属性&#xff1a; &#xff08;1&#xff09;信息存储在何处&#xff1b; &#xff08;2&#xff09;存储的值为多少&#xff1b; &#xff08;3&#xff09;存储的信息…

GitHub爆赞的Web安全防护指南,网络安全零基础入门必备教程!

web安全现在占据了企业信息安全的很大一部分比重&#xff0c;每个企业都有对外发布的很多业务系统&#xff0c;如何保障web业务安全也是一项信息安全的重要内容。 然而Web 安全是一个实践性很强的领域&#xff0c;需要通过大量的练习来建立对漏洞的直观认识&#xff0c;并积累…