用 Swift 制作一个漂亮的汉堡按钮过渡动画
2015-03-25 09:52
363 查看
原文:How
to build a nice Hamburger Button transition in Swift
汉堡按钮在界面设计中已经是老生常谈了,但是当我在dribbble看到这个漂亮的过渡动画时,我决定试试用代码实现它。
这是 CreativeDash team 的原型图:
你可能已经注意到了,汉堡顶部和底部的线条是来自’X’字符,中间的线条变为外框。我知道这种效果可以用CAShapeLayer创建出来,但是首先我得为三条线分别创建一个CGPath。
这种短直线可以手写出来:
对于中间的线条,我用Sketch创建了path,从中间开始,然后无缝地过渡到圆形的外框。
接下来我导出这个 path 为 SVG 文件,然后将它导入到旧的 PaintCode1 中,它将转换文件为 UIBezierPath 代码片段。然后我按照说明改写了部分代码,创建了一个使用 CGPath 描述的对象:
可能有第三方库能够帮助你从 SVG 文件直接加载为 CGPaths,但是这样的 path 我们这里只有这一个,所以用代码写也无所谓啦。
在我的 UIButton 子类中,我添加了三个 CAShapeLayer 属性,并设置它们为相应的 path:
然后我设置了它们三个的样式:
为了正确地计算出它们的边界,我需要将画笔的大小考虑在内。幸运的是,CGPathCreateCopyByStrokingPath 将创建沿用了原先的画笔的轮廓路径,因此它的边框会完全包含 CAShapeLayer 的内容:
由于外框将在稍后围绕其最右边的点旋转,我只好在 layers 布局时设置相应的 anchorPoint:
现在,当按钮改变状态时,它应当显示三条线到新位置的动画。接下来的两个外笔划非常简单。对于顶部的线条,我首先将它向内移动4个points,以保持在中心,然后旋转至负45度,形成半个X:
(底部的线条留给读者作为练习吧。)
中部的线条有点麻烦。为了达到预期的效果,我需要单独为 CAShapeLayer 的 strokeStart 和 strokeEnd 属性添加动画。
首先,我必须弄清楚在两种状态下的这两个属性正确的值。注意,即使是汉堡包状态下,笔画也不是从0开始。由现有路径延伸略微超过外笔划的左边缘,当应用到 timingFunction 后,我们就完美实现了预期的效果:
现在我们只需要创建动画,然后添加它们到 layer 中:
将所有代码组合在一起,结果如下:
我很高兴完成了。你可以在 GitHub 上找到代码,以及follow我的Twitter。
to build a nice Hamburger Button transition in Swift
汉堡按钮在界面设计中已经是老生常谈了,但是当我在dribbble看到这个漂亮的过渡动画时,我决定试试用代码实现它。
这是 CreativeDash team 的原型图:
你可能已经注意到了,汉堡顶部和底部的线条是来自’X’字符,中间的线条变为外框。我知道这种效果可以用CAShapeLayer创建出来,但是首先我得为三条线分别创建一个CGPath。
这种短直线可以手写出来:
let shortStroke: CGPath = { let path = CGPathCreateMutable() CGPathMoveToPoint(path, nil, 2, 2) CGPathAddLineToPoint(path, nil, 28, 2) return path }()
对于中间的线条,我用Sketch创建了path,从中间开始,然后无缝地过渡到圆形的外框。
接下来我导出这个 path 为 SVG 文件,然后将它导入到旧的 PaintCode1 中,它将转换文件为 UIBezierPath 代码片段。然后我按照说明改写了部分代码,创建了一个使用 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 文件直接加载为 CGPaths,但是这样的 path 我们这里只有这一个,所以用代码写也无所谓啦。
在我的 UIButton 子类中,我添加了三个 CAShapeLayer 属性,并设置它们为相应的 path:
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
为了正确地计算出它们的边界,我需要将画笔的大小考虑在内。幸运的是,CGPathCreateCopyByStrokingPath 将创建沿用了原先的画笔的轮廓路径,因此它的边框会完全包含 CAShapeLayer 的内容:
let boundingPath = CGPathCreateCopyByStrokingPath(layer.path, nil, 4, kCGLineCapRound, kCGLineJoinMiter, 4) layer.bounds = CGPathGetPathBoundingBox(boundingPath)
由于外框将在稍后围绕其最右边的点旋转,我只好在 layers 布局时设置相应的 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 的 strokeStart 和 strokeEnd 属性添加动画。
首先,我必须弄清楚在两种状态下的这两个属性正确的值。注意,即使是汉堡包状态下,笔画也不是从0开始。由现有路径延伸略微超过外笔划的左边缘,当应用到 timingFunction 后,我们就完美实现了预期的效果:
let menuStrokeStart: CGFloat = 0.325 let menuStrokeEnd: CGFloat = 0.9 let hamburgerStrokeStart: CGFloat = 0.028 let hamburgerStrokeEnd: CGFloat = 0.111
现在我们只需要创建动画,然后添加它们到 layer 中:
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
将所有代码组合在一起,结果如下:
我很高兴完成了。你可以在 GitHub 上找到代码,以及follow我的Twitter。
相关文章推荐
- 用 Swift 制作一个漂亮的汉堡按钮过渡动画
- 用 Swift 制作一个漂亮的汉堡按钮过渡动画
- 用Xamarin.iOS制作一个漂亮的汉堡按钮过渡动画
- 如何制作一个漂亮的 CSS 按钮
- 用Ajax 1.0制作一个按钮提交后UpdatePanel动画
- 用Ajax 1.0制作一个按钮提交后UpdatePanel动画
- flash上制作一个按钮,控制动画播放、暂停
- Swift - 使用CATransition制作过渡动画(页面切换转场效果)
- [OpenGL] 制作一个有旋转动画的按钮
- 如何用 Sprite Kit 和 Swift 制作一个逃逸游戏-第二部分
- HTML基础 form-input-radio创建一个单选按钮(不漂亮的)
- 如何制作一个漂亮的 Android 应用图标
- 纯Div+Css制作的漂亮点击按钮和关闭按钮
- cocos2d-x开发笔记:获取Sprite上某一个点的透明度,制作不规则按钮
- [Material Design] 教你做一个Material风格、动画的按钮(MaterialButton)
- [Material Design] 教你做一个Material风格、动画的按钮(MaterialButton)
- 用VC++制作播放AVI视频流的动画按钮
- Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)
- 在photoshop中如何制作一个简洁干净的按钮