动画预览

先扯淡

最近手痒又想整点动画玩玩,但是想了几个主意发现稍微复杂一点的手写都一定会累爆。这篇文章记录一下今天折腾的一个方案。说来简单,就是用矢量设计工具舒舒服服的做好设计,然后输出成 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了。。。心塞。。。

继续加特技

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

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

  1. [UWP]用Shape做动画

    相对于WPF/Silverlight,UWP的动画系统可以说有大幅提高,不过本文无意深入讨论这些动画API,本文将介绍使用Shape做一些进度.等待方面的动画,除此之外也会介绍一些相关技巧. 1. 使 ...

  2. UWP应用载入SVG图片的兼容性方案

    原文 UWP应用载入SVG图片的兼容性方案 新版本<纸书科学计算器>的更新点之一,就是优化了表达式的显示方式.在旧版本中,表达式里的符号是用png图片显示的,当用户放大看的时候会发现一些锯 ...

  3. 动态svg图片简单制作

    一.简介 #topics #no-box-shadow-img { box-shadow: none } 博主头像 svg图片格式不同于其它图片格式,svg图片本质上是一个xml文件,它内部是标记语言 ...

  4. Python3.7将普通图片(png)转换为SVG图片格式并且让你的网站Logo(图标)从此”动”起来

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_148 在之前的几篇文章中,介绍了业界中比较火爆的图片技术SVG(Scalable Vector Graphics),比如Iconf ...

  5. window.open()读取本地图片简单使用总结

    最近做了一个项目,需要读取本地图片出来,问了一些人,感觉在数据库中存取路径比较合适,故做此方法. 后台查询出来的路径

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

    在iOS开发中,我们经常需要给视图添加阴影效果,最简单的方法就是通过设置CALayer的shadowColor.shadowOpacity.shadowOffset和shadowRadius这几个属性 ...

  7. Svg图片在asp网站上的使用

    最近需要做一个动态的根据后台的返回数据而动态显示的导航图,然后我就采用了jquery+ajax+SVG矢量图来实现这个功能. 首先,客户给了个ai的矢量图,我对这一块不懂就找以前同事帮我转成了svg图 ...

  8. [UWP]用Shape做动画(2):使用与扩展PointAnimation

    上一篇几乎都在说DoubleAnimation的应用,这篇说说PointAnimation. 1. 使用PointAnimation 使用PointAnimation可以让Shape变形,但实际上没看 ...

  9. python 将png图片格式转换生成gif动画

    先看知乎上面的一个连接 用Python写过哪些[脑洞大开]的小工具? https://www.zhihu.com/question/33646570/answer/157806339 这个哥们通过爬气 ...

随机推荐

  1. 凸包模板 POJ1873

    // 凸包模板 POJ1873 // n=15所以可以按位枚举求凸包,再记录数据 #include <iostream> #include <cstdio> #include ...

  2. font-size的探究

    整理网上的资料 font字体,用px,em,100%,rem?分什么情况考虑? 我们逐渐意识到,我们用px作为文字大小的单位,已经出现很多问题.最主要是体现在用户不能灵活的控制文字的大小. 对于大多数 ...

  3. [转] 編程風格要素-The Elements of Programming Style 中文英文中英對照

    转自: http://www.loliman3000.com/tech/2fe33ce32906f0302412881.php 下面的程序風格規則提煉自Brian Kernighan和P. J. Pl ...

  4. NServiceBus教程-NServiceBus和WCF

    WCF中缺少的最主要的事情是发布/订阅,但为什么你必须建立它自己吗?NServiceBus,你把它弄出来. 下一个重要的事情是容错.异常导致WCF代理休息,需要"刷新"的代码,但调 ...

  5. 代理模式(proxy)

    1.代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般 ...

  6. 第三百五十八天 how can I 坚持

    万事要有度,不要话唠,也不能不说,把握好分寸,今天貌似又说多了. 加了天班,理了个发,还有老爸明天来北京. 还有同学聚会没去,还有金龙让去吃鱼,没去. 还有.小米视频通话还行,能远程控制桌面, 还有, ...

  7. Xcode环境配置mysql

    本文默认mysql安装目录为/usr/local/mysql 在项目的header search paths中添加/usr/local/mysql/includ 2.在项目的library searc ...

  8. 深入学习JavaScript对象

    JavaScript中,除了五种原始类型(即数字,字符串,布尔值,null,undefined)之外的都是对象了,所以,不把对象学明白怎么继续往下学习呢? 一.概述 对象是一种复合值,它将很多值(原始 ...

  9. pyqt中QDateTimeEdit/QDateEdit相关使用方法

    QDateTimeEdit/QDateEdit clear (self)QDate date (self)QDateTime dateTime (self)setDate (self, QDate d ...

  10. ASP.NET前台AJAX方法调用后台的方法写法

    前台: <input id="AjaxDemo" type="button" onclick="get()" value=" ...