[Swift] 如何用Swift做一个不错的按钮变换动画

汉堡按钮在 UI 设计中早已不是什么新鲜玩意儿了,但是某天我突然在 dribbble 上看到了这个酷炫的动画效果,效果真是棒棒哒!于是我决定把它在代码里实现一下。

先来看下 CreativeDash 团队做出来的原始动画效果:

我们可以看到 (看得我眼睛都花了),汉堡按钮 (也就是三条横线的那个)的上下两条线分别绕着自身最优的端点旋转了45°,变成了按钮里的 X ,中间的那个线则摇身一变,成了外面的圈圈。这个效果可以用 CAShapeLayer 实现,但是首先,我需要为这三条直线分别创建一个 CGPath 。

我们可以这样手动创建一个短短的直线:

let shortStroke: CGPath = {
    let path = CGPathCreateMutable()
    CGPathMoveToPoint(path, nil, 2, 2)
    CGPathAddLineToPoint(path, nil, 28, 2)

    return path
}()

不过对于中间那个线,因为它有比较复杂的运动轨迹,所以我用 Sketch 创建了一条路径,把动画融入了线条中:

接下来我把图片导出成了 SVG 格式,然后通过 PaintCode 1 把图片转换成代码片段,导入项目中。接下来我重写了导入的代码片段,并且创建了想要的 CGPath 对象:

let outline: CGPath = {
    let path = CGPathCreateMutable()
    CGPathMoveToPoint(path, nil, 10, 27)
    CGPathAddCurveToPoint(path, nil, 12.00, 27.00, 28.02, 27.00, 40, 27)
    CGPathAddCurveToPoint(path, nil, 55.92, 27.00, 50.47,  2.00, 27,  2)
    CGPathAddCurveToPoint(path, nil, 13.16,  2.00,  2.00, 13.16,  2, 27)
    CGPathAddCurveToPoint(path, nil,  2.00, 40.84, 13.16, 52.00, 27, 52)
    CGPathAddCurveToPoint(path, nil, 40.84, 52.00, 52.00, 40.84, 52, 27)
    CGPathAddCurveToPoint(path, nil, 52.00, 13.16, 42.39,  2.00, 27,  2)
    CGPathAddCurveToPoint(path, nil, 13.16,  2.00,  2.00, 13.16,  2, 27)

    return path
}()

可能有第三方的类库可以让你直接把 SVG 转换成 CGPath,但是对于这种简单的图像,没必要杀鸡用牛刀。

在我自定义的 UIButton 的子类里,我添加了三个 CAShapeLayer 属性并且把他们和前面定义的路径关联了起来:

self.top.path = shortStroke
self.middle.path = outline
self.bottom.path = shortStroke

然后设置一下相关的属性:

layer.fillColor = nil
layer.strokeColor = UIColor.whiteColor().CGColor
layer.lineWidth = 4
layer.miterLimit = 4
layer.lineCap = kCALineCapRound
layer.masksToBounds = true

为了正确的计算出边框 (bounds) ,我需要对这些线条的大小进行计算。谢天谢地,用CGPathCreateCopyByStrokingPath 这个函数可以创建一条和原来线条一样的路径,所以它的 bounds 和参数里那个CAShapeLayer 类型的内容完全一致:

let boundingPath = CGPathCreateCopyByStrokingPath(layer.path, nil, 4, kCGLineCapRound, kCGLineJoinMiter, 4)

layer.bounds = CGPathGetPathBoundingBox(boundingPath)

因为汉堡按钮的上下两个线条接下来会绕着最右点旋转,所以在布局图层的时候,我们需要相应的设置它的锚点 (anchorPoint):

self.top.anchorPoint = CGPointMake(28.0 / 30.0, 0.5)
self.top.position = CGPointMake(40, 18)

self.middle.position = CGPointMake(27, 27)
self.middle.strokeStart = hamburgerStrokeStart
self.middle.strokeEnd = hamburgerStrokeEnd

self.bottom.anchorPoint = CGPointMake(28.0 / 30.0, 0.5)
self.bottom.position = CGPointMake(40, 36)

接下来就是动画的部分了。当按钮的状态改变的时候,三条直线应该会动态的移动到新的位置。对于那两个外围的直线而言,实现起来很容易。比如最上面的那条线,我先把它往里移动 4 points 让它位于中心位置,然后再逆时针旋转45度,形成了半个 X :

var transform = CATransform3DIdentity
transform = CATransform3DTranslate(transform, -4, 0, 0)
transform = CATransform3DRotate(transform, -M_PI_4, 0, 0, 1)

let animation = CABasicAnimation(keyPath: "transform")
animation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
animation.toValue = NSValue(CATransform3D: transform)
animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.8, 0.75, 1.85)
animation.beginTime = CACurrentMediaTime() + 0.25

self.top.addAnimation(animation, forKey: "transform")
self.top.transform = transform

最下面的那条线的部分交给读者朋友们作为练习。

接下来,中间的那条直线似乎有点棘手。为了实现理想的效果,我们需要给 CAShapeLayer 的起点和终点分别设置动画效果。

首先我需要知道这两个属性在两种状态下的值分别是多少。注意,就算是在汉堡状态下,直线也没有从 O 开始。路径稍微的从左边延伸,我们可以稍候应用 timing 函数实现一个酷炫的效果:

let menuStrokeStart: CGFloat = 0.325
let menuStrokeEnd: CGFloat = 0.9

let hamburgerStrokeStart: CGFloat = 0.028
let hamburgerStrokeEnd: CGFloat = 0.111

接下来我们只需要创建动画并加到图层里即可:

let strokeStart = CABasicAnimation(keyPath: "strokeStart")
strokeStart.fromValue = hamburgerStrokeStart
strokeStart.toValue = menuStrokeStart
strokeStart.duration = 0.5
strokeStart.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1)

self.middle.addAnimation(strokeStart, forKey: "strokeStart")
self.middle.strokeStart = menuStrokeStart

let strokeEnd = CABasicAnimation(keyPath: "strokeEnd")
strokeEnd.fromValue = hamburgerStrokeEnd
strokeEnd.toValue = menuStrokeEnd
strokeEnd.duration = 0.6
strokeEnd.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1)

self.middle.addAnimation(strokeEnd, forKey: "strokeEnd")
self.middle.strokeEnd = menuStrokeEnd

把前面的工作整合一下,你可以看到如下的结果:

哈哈哈哈成功实现了,洒家真是太高兴了。你可以在这里下载源码。当然你也可以加我的小鸟。玩的开心~



原文地址

时间: 2024-08-02 09:23:27

[Swift] 如何用Swift做一个不错的按钮变换动画的相关文章

图文详解-如何用Axure做一个倒计时功能按钮

本篇主要给大家讲一下如何用Axure巧妙简单的实现一个倒计时功能. demo地址: http://pan.baidu.com/s/1jI4IRzC 密码: 8ghd 需要更详细学习的同学可以看下面文章.偷懒的同学可以直接在demo里复制出想要的部分即可使用了. 1.把主要元素进行排版设计: 这里每个人有每个人的习惯,没有一个标准,只是将主要元素进行组织后加以排版.大家发现并没有获取验证码按钮,这是我们的关键.下一步开始进行添加 2.添加一个Dynamic Panel(动态面板)命名为getCod

【CSS】如何用css做一个爱心

摘要:HTML的标签都比较简单,入门非常的迅速,但是CSS是一个需要我们深度挖掘的东西,里面的很多样式属性掌握几个常用的便可以实现很好看的效果,下面我便教大家如何用CSS做一个爱心. 前期预备知识: 明白正方形的画法. 明白圆形的画法. 明白什么是定位. 明白怎么旋转. 话不多说,先教大家怎么用css画一个圆形. .disc1{ width: 100px; height: 100px; border:1px solid red; background-color: red; margin:300

如何用C#做一个悬浮窗口程序

用C#做一个像FlashGet的悬浮窗口,其实很简单,不像以前需要调用很多系统API.大致的步骤如下. 首先是主窗体部分,即要判断窗体的状态来决定是否显示悬浮窗口. 局部成员声明: private FormWindowState fwsPrevious; private frmTopMost myTopMost; 主窗体的Load事件: private void frmMain_Load(object sender, System.EventArgs e) { // Save window st

uwp - 做一个相对炫酷的动画按钮/按钮动画

看腻了系统自带的button animation何不尝试下自定义一个较为炫酷的动画顺便提升用户体验.效果图: 动画分为几个部分,分别是:内圆从中心放大(1)并同时渐隐(2),外圆从中心放大(3)并同时渐隐(4),按钮整体从中心缩小放大(5),非常简单对吧,代码也是. 为了方便调用,我用一个自定义用户控件来做,图标是用iconfont,这样可以在任何地方使用,首先新建[AnimationButton.xaml]用户控件,前台代码将自动生成的部分替换: <UserControl.Resources>

如何用RadioButton做一个底部的切换栏

上面的效果是用Radio进行制作的,一般我们做底部的切换栏的时候需要让按钮和文字都有一个选中的状态,然后根据点击不同的按钮触发不同的页面,这里的页面一般都是fragment做的.这里我们不讨论复杂的东西,只是讲如何实现这样的效果.这里的关键点是中间的按钮和两边的按钮没有互斥关系,仅仅是一个独立的ImageView,还有就是按钮的文字需要根据选中的状态进行变化,按钮的图片需要根据状态进行变化. 一.定义按钮的图片和文字效果 我们在res中建立一个color的目录,建立一个main_bottomba

如何用SVG写一个环形进度条以及动画

本次案例主要使用了svg的三个元素,分别为circle.text.path,关于svg的介绍大家可以看MDN上的相关教程,传送门 由于svg可以写到HTML中,所以这里我们就可以很方便的做进度条加载动画啦,这次案例以vue来做数据交互 svg的viewBox viewBox属性定义了画布上可以显示的区域,viewBox有4个值,值1为svg窗口起点显示的x坐标,值2位svg窗口起点的y坐标, 后面的两个分别为显示的大小; 平常可以通过后面这两个值对svg图形进行放大和缩小, 前面的两个值进行窗口

转 。。。。一个不规则的按钮 虽然已经不适用于cocos2dx3.0以上版本 but思路就应该是这个样子滴

本篇文章主要讲一下怎么做一个不规则的按钮,比如如下图的八卦,点击绿色和点击红色部分,需要执行不同的事件 一般情况下,如果要检测某一个精灵是否被点中,做法如下 bool HelloWorld::ccTouchBegan(cocos2d::CCTouch* pTouch, cocos2d::CCEvent* pEvent) { CCSize sprSize = pSpr->getContentSize(); CCPoint point = pSpr->convertTouchToNodeSpace

Swift:用UICollectionView整一个瀑布流

本文的例子和Swift版本是基于Xcode7.2的.以后也许不知道什么时候会更新. 我们要干点啥 用新浪微博的Open API做后端来实现我们要提到的功能.把新浪微博的内容,图片和文字展示在collection view中.本文只简单的展示内容.下篇会用pinterest一样的效果来展示这些内容. 我们准备优先展示图片.你的好友花了那么多时间拍照或者从相册里选择图片发上来多不容易.如果微博返回的数据中有中等大小的缩略图,那么久展示这个缩略图.否则的话显示文本.文本都没有的话...这个就不是微博了

Swift基础:创建第一个Swift工程:Swift的基本语法

苹果公司在今年的WWDC大会上,除了发布了iOS8和Max OS X 10.10之外,还发布了一个新的编程语言Swift.不得不说,Swift有较大的转变,也使编程变得更加容易了,下面介绍了Swift的常量和变量的定义,基本控制语句的使用: 需要注意的是:Swift只能在Xcode 6 Beta版中运行,Xcode 6 目前最新的是Beta 7,大家可以通过以下链接下载: http://adcdownload.apple.com//Developer_Tools/xcode_6_beta_7_a