美文网首页动画
使用CAShapeLayer实现浮动的汉堡层菜单功能

使用CAShapeLayer实现浮动的汉堡层菜单功能

作者: 大尾巴熊Johnny | 来源:发表于2016-01-21 13:16 被阅读277次

    原文链接

    CALayer.gif

    简介

    因为我想继续写一些博文并借此学习更多的swift知识,所以我决定去实现一些在Dribble里关于iOS评价高的UIX交互。

    让我们先来看看我是如何来实现‘Floating burger 2.0 by Eddie Lobanovskiy’效果的。

    实现方法

    CAShapeLayer能用来渲染CGPath,虽然CAShapeLayer有许多的属性,但是为了实现这个简单的交互,我们只需要设置2个属性:

    • strokeStartstrokeEnd,它们能用来控制实际绘制的路径长度

    如果我们让线条更好插入屏幕中,我们把所有的路径看成单一的路径

    Path.png

    这意味着,对于我们动画的两种状态我们能使用strokeStartstrokeEnd来分别渲染特定的部分。

    当我们创建路径的时候,我们需要小心处理。

    线条路径

    通过使用这两个属性,我们能够完全控制形状的展示效果,它能更好的手动地组成我们的CGPath,而不是用类似于addArcWithCenter

    好在我们的路径只是一条直线和一个圆:

    
    let path = UIBezierPath()
    
    let radius: CGFloat = 20
    
    let inset: CGFloat = 30
    
    let lineLength = viewportWidth() - inset
    
    let lineStart = (viewportWidth() - (lineLength - radius)) / 2
    
    path.moveToPoint(CGPoint(x: lineStart, y: 0))
    
    path.addLineToPoint(CGPoint(x: lineLength, y: 0))
    
    let circleCenter = CGPoint(x:lineLength, y: -radius)
    
    var nextPoint = CGPointZero
    
    let _ = (0..<360).map {
    
    nextPoint = CGPoint(x: CGFloat(sinf(toRadian($0))) * radius + circleCenter.x, y: CGFloat(cosf(toRadian($0))) * radius + circleCenter.y)
    
    path.addLineToPoint(nextPoint)
    
    }
    
    

    首先我们创建一个水平线条,然后利用基本数学运算的sin/cos来组成一个圆。

    因为strokeEndstrokeStart是使用标准化坐标(0-1),所以我们需要标准化我们线条的长度。

    
    let circleLength = 2.0 * CGFloat(M_PI) * radius
    
    let totalLength = circleLength + lineLength - lineStart
    
    let lineLengthNormalized = (lineLength - lineStart) / totalLength
    
    

    从而得到该值

    
    lineLengthNormalized = 0.67833278554572718
    
    

    动画

    通过如下配置

    
    shapeLayer.strokeStart = 0
    
    shapeLayer.strokeEnd = lineLengthNormalized
    
    

    我们得到我们的第一个动画状态。

    StrokeEnd.png

    通过如下配置

    
    shapeLayer.strokeStart = lineLengthNormalized
    
    shapeLayer.strokeEnd = 1
    
    

    我们得到折叠收起状态

    StrokeStart.png

    下面只剩下一个任务,就是让这两种状态动起来。

    你需要CoreAnimation里的CABasicAnimationstrokeStartstrokeEnd都用相同的动画类型来表达。

    给出如下函数原型:

    
    func animate(shape: CAShapeLayer, duration: CFTimeInterval, stroke: (start: CGFloat, end: CGFloat), headerAlpha: CGFloat)
    
    

    对每个属性的动画如下:

    
    //! 1
    
    let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
    
    strokeEndAnimation.fromValue = shape.strokeEnd
    
    strokeEndAnimation.toValue = stroke.end
    
    strokeEndAnimation.duration = duration
    
    strokeEndAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    
    strokeEndAnimation.fillMode = kCAFillModeBoth
    
    //! 2
    
    shape.strokeEnd = stroke.end
    
    shape.addAnimation(strokeEndAnimation, forKey: strokeEndAnimation.keyPath
    
    

    但在此重要的一点就是如何确定我们的模型和显示的匹配:CoreAnimation有2层,model和presentationLayer

    当你执行CAAnimation你只能修改显示部分。

    当动画结束后,你会发现你的对象有变回之前旧的配置。

    为了保持model和presentation(显示)同步:

    • 设置fromValue到当前layer上

    • 运行动画之前,设置layer值到我们的目标toValue

    再滚动时,触发两种状态的代码如下:

    
    func scrollViewDidScroll(scrollView: UIScrollView) {
    
    let deadZone = (start: CGFloat(10), end: CGFloat(50))
    
    if (scrollView.contentOffset.y < deadZone.start && isCollapsed) {
    
    animateToOpen(headerShapeLayer, duration: 0.25)
    
    } else
    
    if (scrollView.contentOffset.y > deadZone.end && !isCollapsed) {
    
    animateToCollapsed(headerShapeLayer, duration: 0.25)
    
    }
    
    }
    
    

    总结

    UIKit和CoreGraphics都非常有用,它能让我们写很少的代码就能实现非常好的特效。

    所以值得我们投入更多的时间来学习CoreGraphics。确保先阅读更多关于CALayer组件的强大。

    Github源码

    参考:

    相关文章

      网友评论

        本文标题:使用CAShapeLayer实现浮动的汉堡层菜单功能

        本文链接:https://www.haomeiwen.com/subject/momlkttx.html