美文网首页
iOS 贝塞尔曲线路径动画 SVG快速实现(Swift版)

iOS 贝塞尔曲线路径动画 SVG快速实现(Swift版)

作者: 老坛泡菜 | 来源:发表于2019-02-25 15:54 被阅读25次

    本文将简单实现iOS快速路径绘制动画。

    什么核心动画(Core Animation)、CAShapeLayer、UIBeizerPath等这些,这篇文章就不多说了。
    反正看完这篇文章可以实现以下两种效果,虽然这两种效果很弱智,但高楼从地起嘛,懂了原理可以实现越来越复杂的动画组。Demo在最后可以下载,代码很烂,大神轻喷。



    注:二维码效果来源:文章,因为原文里的部分功能无法重现,故在此重新写了个Demo。

    案例一:快速实现路径动画

    • 步骤:

      1.方案有很多,可以通过Sketch软件,先绘制出想要的图,然后导出SVG,再用PaintCode软件将SVG文件转换成所需的代码。这里我直接通过PaintCode绘制路径图,导出代码啦。
      2.打开PaintCode,使用钢笔工具,随便画一条线,在软件的下方会直接生成贝塞尔曲线的代码(PS:也可以根据自己需要更改所使用的编程语言)。

      3.复制生成的代码,去Xcode创建一个UIView。

    class ESSVGView: UIView {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            backgroundColor = UIColor.white
            drawBezierPath()
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        func drawBezierPath() {
           
            //通过PaintCode生成的代码
            let pathPath = UIBezierPath()
            pathPath.move(to: CGPoint(x: 179.5, y: 36.5))
            pathPath.addCurve(to: CGPoint(x: 52.5, y: 118.5), controlPoint1: CGPoint(x: 95.5, y: 81.5), controlPoint2: CGPoint(x: 52.5, y: 118.5))
            pathPath.addLine(to: CGPoint(x: 263.5, y: 118.5))
            pathPath.addCurve(to: CGPoint(x: 75.5, y: 253.5), controlPoint1: CGPoint(x: 263.5, y: 118.5), controlPoint2: CGPoint(x: 4.5, y: 205.5))
            pathPath.addCurve(to: CGPoint(x: 263.5, y: 253.5), controlPoint1: CGPoint(x: 146.5, y: 301.5), controlPoint2: CGPoint(x: 263.5, y: 253.5))
            pathPath.addCurve(to: CGPoint(x: 75.5, y: 378.5), controlPoint1: CGPoint(x: 263.5, y: 253.5), controlPoint2: CGPoint(x: 222.5, y: 413.5))
            pathPath.addCurve(to: CGPoint(x: 134.5, y: 527.5), controlPoint1: CGPoint(x: -71.5, y: 343.5), controlPoint2: CGPoint(x: 134.5, y: 527.5))
            pathPath.addLine(to: CGPoint(x: 263.5, y: 404.5))
            
            //添加path到ShapeLayer
            let pathLayer = CAShapeLayer()
            pathLayer.frame = frame
            pathLayer.bounds = frame
            pathLayer.isGeometryFlipped = false
            pathLayer.path = pathPath.cgPath
            pathLayer.strokeColor = UIColor.red.cgColor
            pathLayer.fillColor = nil
            pathLayer.lineWidth = 3
            pathLayer.lineJoin = CAShapeLayerLineJoin.bevel
            layer.addSublayer(pathLayer)
            
            //添加动画
            let animation = CABasicAnimation(keyPath: "strokeEnd") //strokeEnd正常绘制效果,strokeStart逐渐消失效果
            animation.fromValue = 0 //初始值
            animation.toValue = 1 //结束值
            animation.repeatCount = 10 //重复次数
            animation.duration = 10 //动画时间
            pathLayer.add(animation, forKey: nil)
            
        }
    
    }
    

    4.接着去ViewController,添加到视图里就OK了。简单粗暴!

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let svg = ESSVGView(frame: view.frame)
            view.addSubview(svg)
        }
    
    }
    

    案例二:快速实现二维码动画

    • 步骤:
      1.首先找一个可以生成二维码并能导出SVG的网站,这里我用的是草料二维码生成,选择其他格式。

    2.下载SVG格式文件



    3.这里可以用文本编辑打开一下这个SVG文件,其实里面就是个XML,注意这些use就是二维码中一个个小方块。
    每个方块x,y都知道了,width和height是固定值都是12。


    4.接下来将SVG格式文件拖到工程,并用XML来解析这个文件

     override init(frame: CGRect) {
            super.init(frame: frame)
            backgroundColor = UIColor.white
            let qrPath = Bundle.main.path(forResource: "qr", ofType: "svg")!
            let qrData = NSData(contentsOfFile: qrPath)
            let xmlParser = XMLParser(data: qrData! as Data)
            xmlParser.delegate = self
            xmlParser.parse()
        }
    

    5.使用XML代理的2个方法进行处理

    每当解析到一个新标签,这里就会被调用
     func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:])
    
    整个 svg 文件解析完毕后,这里就会被调用
    func parserDidEndDocument(_ parser: XMLParser)
    

    6.最后我就直接把代码贴上来,关键地方都注释了干啥滴。

    class ESXML: UIView {
        
        var rects = [CGRect]()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            backgroundColor = UIColor.white
            //解析SVG文件
            let qrPath = Bundle.main.path(forResource: "qr", ofType: "svg")!
            let qrData = NSData(contentsOfFile: qrPath)
            let xmlParser = XMLParser(data: qrData! as Data)
            xmlParser.delegate = self
            xmlParser.parse()
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    
    extension ESXML: XMLParserDelegate {
        
        func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
            if elementName == "use" {
                //但elementName等于use的时候,记录rect并添加的rects数组中
                let x = Double(attributeDict["x"]!)
                let y = Double(attributeDict["y"]!)
                let rect = CGRect(x: x!, y: y!, width: 12, height: 12)
                rects.append(rect)
            }else if elementName == "svg" {
                //elementName等于svg的时候,记录这个二维码大小
                let w = Double(attributeDict["width"]!)
                let h = Double(attributeDict["height"]!)
                bounds = CGRect(x: 0, y: 0, width: w!, height: h!)
            }
        }
        
        func parserDidEndDocument(_ parser: XMLParser) {
            //给layer添加阴影
            layer.shadowColor = UIColor.gray.cgColor
            layer.shadowRadius = 4
            layer.shadowOpacity = 1
            layer.shadowOffset = CGSize.zero
            
            //遍历rects数组,把每一个rect变为一个贝塞尔路径,并用一个CAShapeLayer承载,最后添加动画。
            for r in rects {
                let rectLayer = CAShapeLayer()
                
                rectLayer.fillColor = UIColor.orange.cgColor
                rectLayer.strokeColor = nil
                rectLayer.path = UIBezierPath(rect: CGRect(origin: CGPoint.zero, size: r.size)).cgPath
                rectLayer.frame = r
                
                var startTransform = CATransform3DIdentity
                startTransform.m34 = 1.0 / -20  // 透视
                startTransform = CATransform3DRotate(startTransform, CGFloat(Double.pi)*0.5, 0, 1, 0)  // 沿 y 轴旋转 π/2 圈,待会再动画转回来
    
                // transform 动画
                let transAnim = CABasicAnimation(keyPath: "transform")
                transAnim.duration = drand48() * 2  // 随机一个持续时间
                transAnim.fromValue = NSValue(caTransform3D: startTransform)
                transAnim.toValue = NSValue(caTransform3D: CATransform3DIdentity)
                transAnim.repeatCount = 5
                rectLayer.add(transAnim, forKey: "transAnim")
    
                // 透明度动画
                let alphaAnim = CABasicAnimation(keyPath: "opacity")
                alphaAnim.duration = transAnim.duration
                alphaAnim.fromValue = 0
                alphaAnim.toValue = 1
                alphaAnim.repeatCount = 5
                rectLayer.add(alphaAnim, forKey: "alphaAnim")
                
                layer.addSublayer(rectLayer)
            }
        }
    }
    

    Demo下载地址

    相关文章

      网友评论

          本文标题:iOS 贝塞尔曲线路径动画 SVG快速实现(Swift版)

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