读取svg图片为UIBezierPath,开心做动画

动画预览

先扯淡

最近手痒又想整点动画玩玩,但是想了几个主意发现稍微复杂一点的手写都一定会累爆。这篇文章记录一下今天折腾的一个方案。说来简单,就是用矢量设计工具舒舒服服的做好设计,然后输出成 svg 格式,再用 NSXMLParser 去读出来,转换成 UIBezierPath ,然后就天高任鸟飞~

清晰起见,这里不使用各种库,由上面的二维码动画为例,只转换最简单的矩形。需要更多高能操作的,出门右转 SVGKit

开工

筹备材料先,首先找个能提供 svg 格式下载的二维码生成网站,比如 这个 。拿到 svg 文件后用文本编辑器打开可以看到其实是一个描述矢量图形的 xml ,而且里面几百个矩形。。。如果你用的生成网站跟我一样,还会有一个白色的背景矩形,待会儿我们会把它排除掉。

准备工作就到这了,接下来我们会用 NSXMLParser 来解析这个二维码。

新建一个 Single View Application ,把二维码拖进项目里去,在 ViewController 里添加一个 UIView 作为二维码的容器:

class ViewController: UIViewController {
  let qrView = UIView()
  override func viewDidLoad() {
    super.viewDidLoad()
    qrView.center = view.center
    view.addSubview(qrView)
  }
  ...
}

初始化一个 NSXMLParser 去解析 svg ,同时让 ViewController 实现 NSXMLParserDelegateparser(_:didStartElement:namespaceURI:qualifiedName:attributes:)parserDidEndDocument(_:) 两个方法用于处理解析结果:

class ViewController: UIViewController, NSXMLParserDelegate {
  ...
  override func viewDidLoad() {
    ...
    let qrPath = NSBundle.mainBundle().pathForResource("zcfan_qrcode", ofType: "svg")!
    let qrData = NSData(contentsOfFile: qrPath)
    let xmlParser = NSXMLParser(data: qrData)
    xmlParser.delegate = self
    xmlParser.parse()
  }
  func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
    // 每当解析到一个新标签,这里就会被调用
  }
  func parserDidEndDocument(parser: NSXMLParser!) {
    // 整个 svg 文件解析完毕后,这里就会被调用
  }
  ...
}

接下来我们会在 parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) 中把遇到的每一个形如:

<rect ... x="0" y="0" width="12" height="12" fill="black"/>

的标签转换成 CGRect 保存在数组中,并在 parserDidEndDocument(_:) 中把他们转换为 CAShapeLayer 并添加动画。

先来看看 parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) 的内容:

...
var rects = [CGRect]()  // 用于存储二维码
func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
  // 只转换 “黑色” 的二维码 “方块”
  if elementName == "rect" && (attributeDict["fill"] as String) == "black" {
    let x = (attributeDict["x"] as NSString).doubleValue
    let y = (attributeDict["y"] as NSString).doubleValue
    let w = (attributeDict["width"] as NSString).doubleValue
    let h = (attributeDict["height"] as NSString).doubleValue
    let rect = CGRect(x: x, y: y, width: w, height: h)
    rects.append(rect)
  // 设置 qrView 的尺寸为 svg 图像的大小
  } else if elementName == "svg" {
    let w = (attributeDict["width"] as NSString).doubleValue
    let h = (attributeDict["height"] as NSString).doubleValue
    qrView.bounds = CGRect(x: 0, y: 0, width: w, height: h)
  }
}
...

接下来是 parserDidEndDocument(_:) ,在这里我们要把二维码显示出来:

...
func parserDidEndDocument(parser: NSXMLParser!) {
    for r in rects {
  let rectLayer = CAShapeLayer()
  rectLayer.fillColor = UIColor.darkGrayColor().CGColor
  rectLayer.strokeColor = nil
  rectLayer.path = UIBezierPath(rect: CGRect(origin: CGPointZero, size: r.size)).CGPath  // #1
  rectLayer.frame = r  // #2
  qrView.layer.addSublayer(rectLayer)
    }
}
...

#1、 #2 : 看着有点晕?代码不直观的话不妨稍微把玩一下,原因很简单,但要用语言解释我的舌头可能会打结。。。

至此,运行项目应该就能在屏幕上看到一个大二维码了!

加特技! Duang~

回到上面的 parserDidEndDocument(_:) 方法,然后把它改到面目全非!Duang~

func parserDidEndDocument(parser: NSXMLParser!) {
    qrView.layer.shadowColor = UIColor.grayColor().CGColor
    qrView.layer.shadowRadius = 4
    qrView.layer.shadowOpacity = 1
    qrView.layer.shadowOffset = CGSizeZero
    for r in rects {
  let rectLayer = CAShapeLayer()
  rectLayer.fillColor = UIColor.darkGrayColor().CGColor
  rectLayer.strokeColor = nil
  rectLayer.path = UIBezierPath(rect: CGRect(origin: CGPointZero, size: r.size)).CGPath
  rectLayer.frame = r
  var startTransform = CATransform3DIdentity
  startTransform.m34 = 1.0 / -20  // 透视
  startTransform = CATransform3DRotate(startTransform, CGFloat(M_PI)*0.5, 0, 1, 0)  // 沿 y 轴旋转 π/2 圈,待会再动画转回来
  // transform 动画
  let transAnim = CABasicAnimation(keyPath: "transform")
  transAnim.duration = drand48() * 4  // 随机一个持续时间
  transAnim.fromValue = NSValue(CATransform3D: startTransform)
  transAnim.toValue = NSValue(CATransform3D: CATransform3DIdentity)
  rectLayer.addAnimation(transAnim, forKey: "transAnim")
  // 透明度动画
  let alphaAnim = CABasicAnimation(keyPath: "opacity")
  alphaAnim.duration = transAnim.duration
  alphaAnim.fromValue = 0
  alphaAnim.toValue = 1
  rectLayer.addAnimation(alphaAnim, forKey: "alphaAnim")
  qrView.layer.addSublayer(rectLayer)
    }
}

完工

没眼看,不录gif了。。。心塞。。。

继续加特技

手贱没忍住。。。二维码真是玩不坏。。。

时间: 2024-10-03 08:55:57

读取svg图片为UIBezierPath,开心做动画的相关文章

让CALayer的shadowPath跟随bounds一起做动画改变-b

在iOS开发中,我们经常需要给视图添加阴影效果,最简单的方法就是通过设置CALayer的shadowColor.shadowOpacity.shadowOffset和shadowRadius这几个属性可以很方便的为 UIView 添加阴影效果.但是如果单用这几个属性会导致离屏渲染(Offscreen Rendering),而且CoreAnimation在每一帧绘制阴影的时候都需要递归遍历所有sublayer的alpha通道从而精确的计算出阴影的轮廓,这是非常消耗性能的,从而导致了动画的卡顿. 为

沿着贝塞尔曲线做动画

效果图如下: 源码: // // RootViewController.m // // Copyright (c) 2014年 Y.X. All rights reserved. // #import "RootViewController.h" #import "YXGCD.h" @interface RootViewController () @property (nonatomic, strong) GCDTimer *timer; @end @impleme

用杯赛尔曲线(做动画和绘图)

1. 用被塞尔曲线做动画 效果:位置沿着贝瑟尔曲线位置移动,尺寸由大到小,透明度从完全可见过渡到彻底透明. 至于在DrawRect里面绘制贝塞尔曲线,可以直接百度,没有什么难点的. - (void)clickButton:(id)sender { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:btn.center]; [path addCurveToPoint:btn.center controlPoint1:

iOS 特定图片的按钮的旋转动画

最近做的东西中,要为一个有特定图片的按钮添加旋转动画,Demo代码如下: #import "ViewController.h" @interface ViewController () { BOOL flag; } @property (strong, nonatomic) UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; flag

一些SVG 图片向下兼容优雅降级技术

转自:http://www.zhangxinxu.com/wordpress/?p=3678 一.SVG前言 再一次拿可缩放矢量图形SVG(Scalable Vector Graphics)说事,对SVG有所关注的同行应该都知道,IE8-以及Android 2.3默认浏览器是不支持SVG的,实际项目,这些浏览器,至少IE8浏览器还没有到不管不问的时候,因此,在使用视网膜显示友好的SVG同时,我们还要做一点优雅降级,使IE8等考古价值浏览器也能手染余香. 本文内容参考自CSS-tricks SVG

UIImageView做动画

UIImageView *_dogImageView = [[UIImageView alloc] init]; _dogImageView.frame = CGRectMake(0, 100, 300, 200); [self.view addSubview:_dogImageView]; NSMutableArray* imageArray = [[NSMutableArray alloc] init]; for (int i = 1; i <= 20; ++i) {    NSString

Svg图片在asp网站上的使用

最近需要做一个动态的根据后台的返回数据而动态显示的导航图,然后我就采用了jquery+ajax+SVG矢量图来实现这个功能. 首先,客户给了个ai的矢量图,我对这一块不懂就找以前同事帮我转成了svg图形,听说很简单,但是矢量图是封闭的情况下. 然后,我就开始研究这个SVG图形的使用: 1.SVG图形可以放在embed和iframe标签,虽然embed标签在低版本的浏览器里不支持,但是从长久考虑和技术角度个人觉得放到embed标签里更合适. 在这个地方只有一个需要注意的地方,svg图片的路径要和网

【原创】Android 4.4前后版本读取图库图片方式的变化

Android 4.4前后版本读取图库图片方式的变化 本文讲述Android 4.4(KitKat)前后访问图库以及访问后通过图片路径读取图片的变化 Android 4.4(KitKat)以前: 访问图库(方法一): 1 /** 2 * Access the gallery to pick up an image. 3 */ 4 private void startPickPhotoActivity() { 5 Intent intent = new Intent(Intent. ACTION_

读取本地图片写到jsp页面

//读取本地图片写到jsp页面 File file = new File("D:/parking/A区12号.jpg"); ServletOutputStream out = null; InputStream in = null; try { in = new FileInputStream(file); response().setContentType("multipart/form-data"); int tempbyte; out = response()