移动端访问不佳,请访问我的个人博客

前段时间在github上看见一个非常nice的动画效果,可惜是安卓的,想着用swift写一个iOS版的,下下来源代码研究了一下,下面是我写代码的心路历程

先上图和demo的地址

分析动画过程

刚开始看的时候感觉这个动画很炫酷,实现起来应该挺复制的,后来我将gif图逐一分解,浏览了一下安卓的实现过程,大致了解的实现的过程,下面是一些关键的动画步骤:

  1. 第一步是里面图片的缩放动画,使用CALayer配合CAKeyframeAnimation来实现;
  2. 第二步是是里面一个圆环逐渐变大的过程,使用CAShapeLayer配合CAKeyframeAnimation来实现;
  3. 第三步是最外面一层太阳的扩散效果同样也使用CAShapeLayer配合CAKeyframeAnimation来实现;
  4. 最后是闪烁和颜色变化的的效果,使用CABasicAnimationCADisplayLink来实现。

一、缩放动画的实现

这个实现的过程相对而言比较简单,用CALayer做为mask来实现下图心形的图片,然后用CAKeyframeAnimation来实现动画,values的值为[0.4, 1, 0.9, 1],差值器模式为kCAAnimationCubic,下面是实现结果和关键代码:

public func startAnim() {
    let anim = CAKeyframeAnimation(keyPath: "transform.scale")
    anim.duration  = animDuration
    anim.values = [0.4, 1, 0.9, 1]
    anim.calculationMode = kCAAnimationCubic
    maskLayer.add(anim, forKey: "scale")
}

二、圆环扩散动画的实现

首先圆环我们用CAShapeLayer来绘制一个圆环,然后通过CAKeyframeAnimation来改变圆环的path就可以了,下面是实现结果和关键代码:

public func startAnim() {
    let anim = CAKeyframeAnimation(keyPath: "path")
    anim.duration = params.animDuration * 0.1
    let size = frame.size
    let fromPath = UIBezierPath(arcCenter: CGPoint.init(x: size.width/2, y: size.height/2), radius: 1, startAngle: 0, endAngle: CGFloat(M_PI) * 2.0, clockwise: false).cgPath
    let toPath = UIBezierPath(arcCenter: CGPoint.init(x: size.width/2, y: size.height/2), radius: size.width/2 * CGFloat(params.shineDistanceMultiple), startAngle: 0, endAngle: CGFloat(M_PI) * 2.0, clockwise: false).cgPath
    anim.delegate = self
    anim.values = [fromPath, toPath]
    anim.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
    anim.isRemovedOnCompletion = false
    anim.fillMode = kCAFillModeForwards
    shapeLayer.add(anim, forKey: "path")
}

三、太阳的扩散效果实现

首先我们得先算出每个太阳的位置和将要扩散到的位置,然后用CAShapeLayer绘制出太阳,用CAKeyframeAnimation实现扩散的效果,下面是实现后的结果和关键代码 :

public func startAnim() {
    let radius = frame.size.width/2 * CGFloat(params.shineDistanceMultiple*1.4)
    var startAngle: CGFloat = 0
    let angle = CGFloat(M_PI*2/Double(params.shineCount)) + startAngle
    if params.shineCount%2 != 0 {
        startAngle = CGFloat(M_PI*2 - (Double(angle)/Double(params.shineCount)))
    }
    for i in 0..<params.shineCount {
        let bigShine = shineLayers[i]
        let bigAnim = getAngleAnim(shine: bigShine, angle: startAngle + CGFloat(angle)*CGFloat(i), radius: radius)
        let smallShine = smallShineLayers[i]
        var radiusSub = frame.size.width*0.15*0.66
        if params.shineSize != 0 {
            radiusSub = params.shineSize*0.66
        }
        let smallAnim = getAngleAnim(shine: smallShine, angle: startAngle + CGFloat(angle)*CGFloat(i) - CGFloat(params.smallShineOffsetAngle)*CGFloat(M_PI)/180, radius: radius-radiusSub)
        bigShine.add(bigAnim, forKey: "path")
        smallShine.add(smallAnim, forKey: "path")
    }
    let angleAnim = CABasicAnimation(keyPath: "transform.rotation")
    angleAnim.duration = params.animDuration * 0.87
    angleAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    angleAnim.fromValue = 0
    angleAnim.toValue = CGFloat(params.shineTurnAngle)*CGFloat(M_PI)/180
    angleAnim.delegate = self
    add(angleAnim, forKey: "rotate")
}

四、最后再将这些动画通过一定规律结合起来

上图是将之前动画步骤组合起来后的效果,上面的一些代码只是部分代码,全部代码可以去我的github地址上去下在浏览,如果大家喜欢可以点一个start,有更好的想法也可以提出来,大家一起交流一下,最后谢谢大家阅读~~

iOS动画进阶 - 手摸手教你写ShineButton动画的更多相关文章

  1. iOS动画进阶 - 手摸手教你写 Slack 的 Loading 动画

    如果移动端访问不佳,可以访问我的个人博客 前几天看了一篇关于动画的博客叫手摸手教你写 Slack 的 Loading 动画,看着挺炫,但是是安卓版的,寻思的着仿造着写一篇iOS版的,下面是我写这个动画 ...

  2. 手摸手教你如何在 Python 编码中做到小细节大优化

    手摸手教你如何在 Python 编码中做到小细节大优化 在列表里计数 """ 在列表里计数,使用 Python 原生函数计数要快很多,所以尽量使用原生函数来计算. &qu ...

  3. 手摸手教你微信小程序开发之自定义组件

    前言 相信大家在开发小程序时会遇到某个功能多次使用的情况,比如弹出框.这个时候大家首先想到的是组件化开发,就是把弹出框封装成一个组件,然后哪里使用哪里就调用,对,看来大家都是有思路的人,但是要怎样实现 ...

  4. 手摸手教你让Laravel开发Api更得心应手

    https://www.guaosi.com/2019/02/26/laravel-api-initialization-preparation/ 1. 起因 随着前后端完全分离,PHP也基本告别了v ...

  5. 【转】手摸手,带你用vue撸后台 系列一

    前言 说好的教程终于来了,第一篇文章主要来说一说在开始写业务代码前的一些准备工作吧,但这里不会教你webpack的基础配置,热更新怎么做,webpack速度优化等等,有需求的请自行google. 目录 ...

  6. 手摸手带你用Hexo撸博客(一)

    原文地址 手摸手带你用Hexo撸博客(一) 环境搭建 安装 node 狂点下一步 命令行输入此条命令 如果能看到版本号则安装成功 node -v 安装Git (同上) 实在不会的小伙伴百度一下,教程很 ...

  7. 手摸手,和你一起学习 UiPath Studio

    学习 RPA 的路上坑比较多,让我们手摸手,一起走…… 以下是一些学习 UiPath 和 RPA 的资源, 拿走不用谢! UiPath Studio 中文文档 机器人流程自动化其实是很好的概念和技术, ...

  8. 【转】手摸手,带你用vue撸后台 系列二(登录权限篇)

    前言 拖更有点严重,过了半个月才写了第二篇教程.无奈自己是一个业务猿,每天被我司的产品虐的死去活来,之前又病了一下休息了几天,大家见谅. 进入正题,做后台项目区别于做其它的项目,权限验证与安全性是非常 ...

  9. 【转】手摸手,带你用vue撸后台 系列三(实战篇)

    前言 在前面两篇文章中已经把基础工作环境构建完成,也已经把后台核心的登录和权限完成了,现在手摸手,一起进入实操. Element 去年十月份开始用vue做管理后台的时候毫不犹豫的就选择了Elemen, ...

随机推荐

  1. JS-键盘事件之方向键移动元素

    注意三点: 1:事件名称onkeydown. 2:事件加给document,而非window. 3: 把元素的top,left值分别用offsetTop,offsetLeft来设定. <!DOC ...

  2. 【BZOJ4515】[Sdoi2016]游戏 树链剖分+线段树

    [BZOJ4515][Sdoi2016]游戏 Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 1234567 ...

  3. MUI窗口管理

    参考:窗口管理 http://dev.dcloud.net.cn/mui/window/ 页面初始化:在app开发中,若要使用HTML5+扩展api,必须等plusready事件发生后才能正常使用,m ...

  4. 增量式PID的matlab实现

    首先,增量式PID的实现公式: 式中 Δe(k)=e(k)-e(k-1) 进一步可以改写成 式中      . . 为了便于理解,也可写成: 式中e(k)为第k次采样时的设定值与实际值的差,e(k-1 ...

  5. 【巷子】:关于Apply、call、bind的详解

    call方法: 语法:call(thisObj,'',''........) 定义:调用一个对象的一个方法,以另一个对象替换当前对象 说明:call方法可以用来代替另一个对象调用一个方法.call方法 ...

  6. 170804、使用Joda-Time优雅的处理日期时间

    简介 在Java中处理日期和时间是很常见的需求,基础的工具类就是我们熟悉的Date和Calendar,然而这些工具类的api使用并不是很方便和强大,于是就诞生了Joda-Time这个专门处理日期时间的 ...

  7. 用到了yii2 hasMany() 方法,一对多关联

    view页面代码:其中supply,item,price是一个AR类都是一个类,item和prices是一对多关系: [ 'label' => '参考', 'format' => 'htm ...

  8. Java字符编码问题

    今天研究了一下,记录下来 中间用的是redis,可以使用任意其他的io替代,一样的 Test1 String s1 = "我要测试"; String s2 = "I wa ...

  9. 记一次踩坑:使用ksoap-android时造成的okhttp依赖冲突问题

    项目中需要调用webservice接口,android SDK中并没有直接访问webservice接口的方法,于是我引入了ksoap-android的jar包,来实现访问webservice接口.刚开 ...

  10. kubernetes实战(八):k8s集群安全机制RBAC

    1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...