1. 程式人生 > >[swift]讀取svg圖片為UIBezierPath,開心做動畫

[swift]讀取svg圖片為UIBezierPath,開心做動畫

https://segmentfault.com/a/1190000002580541

更新

給對本文感興趣的朋友們推薦個好東西:paintcode

動畫預覽

先扯淡

最近手癢又想整點動畫玩玩,但是想了幾個主意發現稍微複雜一點的手寫都一定會累爆。這篇文章記錄一下今天折騰的一個方案。說來簡單,就是用向量設計工具舒舒服服的做好設計,然後輸出成 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 實現 NSXMLParserDelegate 的 `
parser(_: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了。。。心塞。。。

繼續加特技

手賤沒忍住。。。二維碼真是玩不壞。。。


相關推薦

[swift]讀取svg圖片UIBezierPath開心動畫

https://segmentfault.com/a/1190000002580541 更新 給對本文感興趣的朋友們推薦個好東西:paintcode 動畫預覽 先扯淡 最近手癢又想整點動畫玩玩,但是想了幾個主意發現稍微複雜一點的手寫都一定會累爆。這篇文章

SVG2PNG(前臺個後臺將SVG轉換PNG完美支持IE8下載)--amcharts導出png

doc 項目 commons cep finall 保存 格式 lose 矢量圖形      在項目中用到了amcharts,amcharts圖標統計插件是利用SVG實現的,其自帶下載png功能,但是不支持IE以下瀏覽器。因此研究了SVG轉換為png,最終實現的效果是將a

C語言讀取二進位制檔案讀取結果全部編譯執行都沒有報錯

利用fread讀取二進位制檔案,讀出來的結果全部為零,編譯執行都沒有報錯,程式碼如下, 有人說是大小端的問題,怎麼理解啊?判斷出來的本機器的為little endian,怎麼判斷需不需要轉換啊?要是需要轉換,怎麼轉換啊?在網上找了利用巨集處理進行大小端轉換的程式碼,但是在我這種情況下,怎

C++使用tinyxml來操作DOM物件(以svg格式其他格式都類似操作)

1.首先去下載tinyxml庫,在這裡下載http://sourceforge.net/projects/tinyxml/   。我使用tinyxml作為例子,不用tinyxml2。下載完畢後將兩個標頭檔案tinystr.h和tinyxml.h放到工程的標頭檔案下,幷包含進

mysql日期儲存intmybatisORM對映與java.util.Date的轉換問題

在mysql做資料庫的應用中,日期型別經常回儲存為int(10)型別。方便排序和計算。但是在java中用Date.getTime返回的是13位的Long。並且在實體中我們如果用long來儲存會有諸多不便。所以涉及到了轉換問題。在我的專案中,用的是mybatis做

viewer 視窗控制,圖片懶載入出入場動畫前端框架

1.開發中有這樣的需求,即希望新增在可視區域內觸發的動畫效果2.或者根據視窗位置動態改變樣式3.亦或是懶載入等,使用viewer都能輕鬆實現,一般來說使用viewer能輕鬆實現監控元素距離視窗位置執行任何你想要的回撥github地址:

iOS9中swift判斷相機相冊權限選取圖片頭像

返回 而已 頭像 iss 模式 會有 vid str 編輯模式 在iOS7以後要打開手機攝像頭或者相冊的話都需要權限,在iOS9中更是更新了相冊相關api的調用 首先新建一個swift工程,在SB中放上一個按鈕,並在viewController中拖出點擊事件 ok!按鈕和事

iOS9中Swift判斷相機相簿許可權選取圖片頭像

在iOS7以後要開啟手機攝像頭或者相簿的話都需要許可權,在iOS9中更是更新了相簿相關api的呼叫 首先新建一個swift工程,在SB中放上一個按鈕,並在viewController中拖出點選事件 ok!按鈕和事件設定好以後,我們來引入要用到的庫,判斷攝像頭許可權,需要引入AVFoundat

python3 學習 3:python爬蟲之爬取動態載入的圖片以百度圖片

轉: https://blog.csdn.net/qq_32166627/article/details/60882964 前言: 前面我們爬取圖片的網站都是靜態的,在頁面中右鍵檢視原始碼就能看到網頁中圖片的位置。這樣我們用requests庫得到頁面原始碼後,再用bs4庫解析標籤即可儲存圖片

nrrd格式用Python讀取並儲存png圖片

安裝pynrrd pip install pynrrd pip install git+https://github.com/mhe/pynrrd.git cd pynrrd pip install . 如果報錯了,則輸入 python setup.py install 注意:

Opencv讀取USB網路攝像頭無法顯示影象顯示黑色?

利用Opencv讀取電腦的攝像頭,程式碼如下,一直無法正常執行,搜尋網上的解決方案,均未解決。程式碼語法沒有錯誤,攝像頭也可以開啟,但是就是無法讀取當前幀影象 #include <opencv.hpp> using namespace cv; int main() { Vid

python 將圖片存入mongodb讀取圖片gridfs模組

匯入圖片 引入模組,其中gridfs模組不需要單獨安裝,引入了pymongo即可直接引入 from pymongo import MongoClient from gridfs import * import os #連結mongodb client=MongoClient('localhost',

關於瀏覽器快取問題(圖片更換後頁面仍優先讀取快取)

因為部分需求(跟換頭像,切換輪播圖等等)改變圖片或者本地上傳新圖片,會導致圖片快取問題,從而達不到預期效果,還是載入原圖片查找了一些資料,總結出2個步驟: 1.在圖片的路徑的後面拼接 ‘?內容’ 解決,具體如下 (1)在圖片src路徑後面加上時間戳,使瀏覽

圖片或檔案上傳到伺服器或從伺服器上讀取圖片可根據路徑src回顯展示從伺服器上讀出來)

不需要配置虛擬路徑,存的時候資料庫裡只存了圖片的名稱(隨機重新命名的形式),存在指定伺服器上,取的時候也是根據圖片名稱從伺服器上找到,並用OutputStream 讀出來 前臺頁面(用的bootstrap): html程式碼(可回顯,回顯的時候也是去後臺根據路徑查詢到圖片):

MFC中當儲存點陣圖時圖片全黑的解決辦法

        需要建立相容點陣圖,建立相容DC,然後需要將點陣圖選入DC中,再將螢幕DC的影象拷貝到建立的相容記憶體DC中。 ······ CRect rect1; GetClientRect(&rect1);

網路圖片轉換base64解決跨域問題

function convertImgToBase64(url, callback, outputFormat) { var canvas = document.createElement('CANVAS'), ctx = canvas.getContext(

tensorflw資料寫入tfrecord使用DataSet讀取

#寫入tfrecord def create_tf_record(inputs, labels, tfrecords_filename): writer = tf.python_io.TFRecordWriter(tfrecords_filename)

將PDF轉換wordHTMLSVGXPS並將其儲存

本文我們將演示如何通過呼叫Spire.PDF提供的方法PdfDocument.SaveToStream()將PDF頁面轉換為HTML,Word,SVG,XPS,PDF並將它們儲存為流。從Spire.PDF版本4.3開始,它新支援轉換定義範圍的PDF頁面並將其儲存為流。 將PDF儲存為流 步驟

利用HTML5上傳檔案並顯示在前端預覽圖片

由於專案中有上傳檔案的功能,所以這次單獨拿出來研究研究,我上網查了查,以前都是用iframe,但是自從HTML5出世之後,就可以利用H5的一些特性來上傳檔案了,啥也不說了,我上程式碼了 <!DOCTYPE html> <html lang