http://bbs.reactnative.cn/topic/306/%E5%9C%A8react-native%E4%B8%AD%E4%BD%BF%E7%94%A8arthtml
前半個月搗騰了一下React Native ART
如今手上閒下來了和你們分享一下React Native中的ART使用心得前端
所謂ART,是一個在React中繪製矢量圖形的JS類庫。這個類庫抽象了一系統的通用接口,統一了SVG,canvas,VML這類矢量圖形在 React中的書寫格式。你能夠經過ART將SVG,canvas,VML的矢量圖形拿到React中使用,也能夠把ART反轉回去。(雖然有不少侷限 性,後面會講到)
React Native ART 是react-art在React Native中的移植版,接口幾乎徹底一致,
React Native中的ART很早以前就已經開源了iOS版,最近又在0.18.0中開源了Android版本
由於缺乏官方文檔,一直不爲人所知。react
我在前端頁面切片的時候比較喜歡用SVG,有條件我都儘可能讓設計師出AI的設計稿或者把圖標導成SVG的
由於SVG和圖片相比,優勢太多太多:能夠代碼複用,能夠無損放大,經過合理導出的svg代碼比同等壓縮過的圖片文件大小要小不少,並且svg代碼還能夠經過gzip壓縮
最近在作一個原生內嵌的React Native項目,設計師是按照原生APP的標準把每一個圖標切了3個尺寸給我,感謝React Native中強大的打包工具,能夠很方便的使用這寫圖標。因此項目開始也沒想着要用SVG(當時android的ART還沒開源,這也是一個重要因 素)。
在整個項目完成以後,看了一下,全部圖片在通過壓縮以後加起來有200多K,大小還能夠接受。
可是在項目後期優化過程當中,發現一個使用圖片圖標很是致命的問題:android
<Image />元素在iOS下不是同步渲染的,即便是使用的本地圖片資源!
更過度的是具備相同source
的多個Image也不能在同一幀裏面渲染出來的,以下的5個圖標,在iOS下它們不會同時被渲 染出來,這樣形成的影響是在打開一個列表的時候,看見這些圖標一個接一個無序的跳出來,這個效果若是在iphone4s,5s下看至關的明顯,很噁心,完 全不能忍受!想過各類方法也沒辦法繞過去。
git
首屏的以下幾個圖標以及返回按鈕和logo,還有TabBar裏面的圖標在iOS下都是在界面出現的若干幀以後再無序的跳出來,這樣的效果很很差,一點都沒有原生APP的感受,有一種hybird的既視感。
github
後來等到了0.18.0,Android的ART對外開源了,我想到爲何不用SVG圖標嘗試一下呢?
而後讓設計師把這個圖標:
從新切成SVG給我,而後再把SVG轉成ART。
在iOS下完美渲染!全部圖標和界面文字一塊兒出現,不論是放5個仍是100個。並且對界面的總體渲染速度也沒有任何能感知到的影響。
在安卓下也是一樣的完美,並且生成的ART代碼要比圖片小了不少。npm
<Surface width={50} height={50} > <Group scale={0.23}> <Shape fill={`${this.props.active ? '#ff5942' : '#ccc'}`} d={`M46.31,0H3.69C1.63,0,0,1.63,0,3.69v42.63C0,48.37,1.63,50,3.69,50h42.63c2.05,0,3.69-1.63,3.69-3.69V3.69 C50,1.63,48.37,0,46.31,0z M44.5,22.92l-7.76,7.65l1.8,10.61c0.18,1.07-0.29,2.14-1.21,2.77c-0.51,0.34-1.1,0.52-1.69,0.52 c-0.49,0-0.98-0.12-1.42-0.35l-9.23-4.75l-9.23,4.75c-0.44,0.24-0.94,0.35-1.42,0.35c-0.6,0-1.19-0.17-1.69-0.52 c-0.92-0.63-1.38-1.69-1.21-2.77l1.8-10.61l-7.76-7.65c-0.77-0.76-1.04-1.86-0.69-2.87c0.35-1.01,1.25-1.73,2.34-1.9l10.6-1.55 l4.6-9.43c0.49-1,1.52-1.62,2.66-1.62c1.15,0,2.18,0.64,2.66,1.62l4.6,9.43l10.59,1.55c1.09,0.16,1.99,0.9,2.34,1.9 C45.53,21.06,45.27,22.16,44.5,22.92z`} /> </Group> </Surface>
這裏面的路徑代碼是經過PS CC直接導出的,沒有作任何優化,若是再優化一番能夠再縮小一半。SVG代碼優化後面再作介紹。
下面正式進入正題canvas
安裝ART很簡單
安卓裏面根本不用安裝,0.18.0裏面自帶,直接在js引入既可react-native
import React, {ART} from 'react-native'; const {Surface, Group, Shape} = ART;
iOS裏面的ART是可選的,你須要手動導入xcodeproj文件,以及加入靜態連接庫
可是也很簡單:教程xcode
請你們原諒我,其實我是標題黨,我不會在這裏詳細的講解ART的用法,而是講一個基於ART的SVG庫:react-native-art-svg
爲何我不在這裏講ART呢?緣由以下
上面那段代碼是ART代碼和設計師給我svg代碼其實有一些差別,還要把SVG轉換一次,很不友好,由於ART的元素和SVG元素名稱和用法都不太同樣
在SVG代碼裏面,畫布元素是<svg>而ART裏面是<Surface>
繪製路徑的元素在SVG裏面是<path>,而ART裏面的Path又是另一個東西,在ART裏面須要<Shape>
這都不是最麻煩的地方,一些基礎的SVG圖形<circle>、<rect>、<polygon>...等等的元素ART都是不支持的。
前端切圖,不論是網頁仍是React Native的界面,設計稿和圖標都是設計師給咱們的(部分連設計都一塊兒作了的大神除外)
而設計師給咱們的矢量圖標要麼是AI原圖要麼是SVG,可是最終到咱們手裏,導出來一定是SVG。
因此ART雖然經過抽象接口來統一了SVG,canvas,VML在React中的使用,可是ART提供的接口仍是至關不友好的。而後我就萌發了一個想法,爲何再也不把ART封裝一層,把語法封裝成和SVG的語法呢?
而後react-native-art-svg就誕生了,支持SVG全部經常使用的元素和屬性,svg代碼拿過來幾乎能夠直接使用(後面會添加直接導入svg代碼的功能)
預覽圖以下:
安裝很簡單,由於是純JavaScript的類庫,直接一行搞定
npm i react-native-art-svg --save
經過以下代碼在js中引入
import Svg, { Circle, Ellipse, G, LinearGradient, RadialGradient, Line, Path, Polygon, Polyline, Rect, Symbol, Text, Use, Defs, Stop } from 'react-native-art-svg';
上面的ART代碼等同於以下的代碼
<Svg width={50} height={50} > <G scale={0.23}> <Path fill={`${this.props.active ? '#ff5942' : '#ccc'}`} d={`M46.31,0H3.69C1.63,0,0,1.63,0,3.69v42.63C0,48.37,1.63,50,3.69,50h42.63c2.05,0,3.69-1.63,3.69-3.69V3.69 C50,1.63,48.37,0,46.31,0z M44.5,22.92l-7.76,7.65l1.8,10.61c0.18,1.07-0.29,2.14-1.21,2.77c-0.51,0.34-1.1,0.52-1.69,0.52 c-0.49,0-0.98-0.12-1.42-0.35l-9.23-4.75l-9.23,4.75c-0.44,0.24-0.94,0.35-1.42,0.35c-0.6,0-1.19-0.17-1.69-0.52 c-0.92-0.63-1.38-1.69-1.21-2.77l1.8-10.61l-7.76-7.65c-0.77-0.76-1.04-1.86-0.69-2.87c0.35-1.01,1.25-1.73,2.34-1.9l10.6-1.55 l4.6-9.43c0.49-1,1.52-1.62,2.66-1.62c1.15,0,2.18,0.64,2.66,1.62l4.6,9.43l10.59,1.55c1.09,0.16,1.99,0.9,2.34,1.9 C45.53,21.06,45.27,22.16,44.5,22.92z`} /> </G> </Svg>
這段代碼從SVG轉過來可省心多了,只須要改一下元素的大小寫,把元素名稱改爲大寫字母開頭的既可,屬性名稱改爲駝峯式的命名方式。
通用屬性列表:
屬性名稱 | 默認值 | 描述 |
---|---|---|
fill | '#000' | 內部填充規則(填充顏色,填充漸變圖形) |
fillOpacity | 1 | 填充的透明度 |
stroke | 'none' | 描邊顏色 |
strokeWidth | 1 | 描邊的寬度 |
strokeOpacity | 1 | 描邊的透明度 |
strokeLinecap | 'square' | 描邊線段端點顯示方式 |
strokeLinejoin | 'miter' | 描邊線段鏈接處的顯示方式 |
strokeDasharray | [] | 描邊線段斷點顯示規則 |
x | 0 | 當前圖形x軸偏移量 |
y | 0 | 當前圖形y軸偏移量 |
rotate | 0 | 當前圖形旋轉值 |
scale | 1 | 當前圖形的縮放值 |
origin | 0, 0 | 變形原點(x,y,rotate,scale的變形原點座標) |
originX | 0 | 變形原點x軸座標 |
originY | 0 | 變形原點y軸座標 |
能夠看到,幾乎全部SVG的經常使用屬性均可以支持
不支持fillRule,由於官方認爲fillRule不是SVG,canvas和VML公用的屬性,他們不對這類獨有的屬性進行支持
暫不支持clipPath,由於ART官方還未完成
支持的元素列表:
Svg爲全部矢量圖形的畫布元素,全部Svg內的矢量圖形都會繪製在Svg內部
而且Svg支持viewbox和preserveAspectRatio屬性(關於這兩個屬性的詳解)
<Svg height="100" width="100" > <Rect x="0" y="0" width="100" height="100" fill="black" /> <Circle cx="50" cy="50" r="30" fill="yellow" /> <Circle cx="40" cy="40" r="4" fill="black" /> <Circle cx="60" cy="40" r="4" fill="black" /> <Path d="M 40 60 A 10 10 0 0 0 60 60" stroke="black" /> </Svg>;
上面的代碼會繪製以下的圖形
其中height和width定義了Svg元素的寬高
注意
在react-native-art-svg中,全部元素的數字類型的屬性均可以直接用字符串表示,如:
下面兩段屬性聲明均可以正常使用,這樣寫起來很方便
height="100"
height={100}
<Rect>元素能夠在畫布上繪製一個矩形,矩形位置和大小經過x,y,width,height屬性共同定義
<Svg width="200" height="60" > <Rect x="25" y="5" width="150" height="50" fill="rgb(0,0,255)" strokeWidth="3" stroke="rgb(0,0,0)" /> </Svg>
上面的代碼繪製了一個左上角位於畫布25,5,寬150,高50的矩形
而且使用藍色(rgb(0,0,255))填充(fill)內部背景
用黑色(rgb(0,0,0))進行描邊(stroke),描邊寬度(strokeWidth)爲3
<Circle>元素能夠在畫布上繪製一個圓形,圓型須要cx,cy,r三個屬性,分別對應圓形x軸座標,y軸座標以及半徑
<Svg height="100" width="110" > <Circle cx="50" cy="50" r="50" fill="pink" /> </Svg>
<Ellipse>元素能夠在畫布上繪製一個橢圓,橢圓和圓形相似,只是把圓形的r屬性替換成了分別對應水平半徑和垂直半徑的rx,ry屬性,rx == ry的橢圓是一個圓形
<Svg height="100" width="110" > <Ellipse cx="55" cy="55" rx="50" ry="30" stroke="purple" strokeWidth="2" fill="yellow" /> </Svg>
<Line>元素能夠在畫布上畫一條從x1,y1到x2,y2的直線
<Svg height="100" width="100" > <Line x1="0" y1="0" x2="100" y2="100" stroke="red" strokeWidth="2" /> </Svg>
<Polygon>元素能夠在畫布上畫一個由多個點收尾相接組合而成的多邊形,每一個點的座標由空格分開定義在points屬性中
<Svg height="100" width="100" > <Polyline points="10,10 20,12 30,20 40,60 60,70 95,90" fill="none" stroke="black" strokeWidth="3" /> </Svg>
<Polyline>元素和多邊形類型,只是多邊形的最後一個點和第一個點是相連的,組成的是一個閉合的圖形,多邊線的最後一個點和第一個點沒有鏈接
<Svg height="100" width="100" > <Polyline points="10,10 20,12 30,20 40,60 60,70 95,90" fill="none" stroke="black" strokeWidth="3" /> </Svg>
<Path>的d
屬性內定義了一系列的路徑座標以及繪製規則命令,上面的全部圖形均可以經過Path繪製而成
注意: 上面全部的命令均可以使用小寫字母,大寫字母的命令是使用的座標是絕對值,小寫字母的命令的座標是相對值
<Svg height="100" width="100" > <Path d="M25 10 L98 65 L70 25 L16 77 L11 30 L0 4 L90 50 L50 10 L11 22 L77 95 L20 25" fill="none" stroke="red" /> </Svg>
<Text>元素能夠在畫布上繪製文字圖形
<Svg height="60" width="200" > <Text fill="none" stroke="purple" fontSize="20" fontWeight="bold" x="100" y="20" textAnchor="center" >STROKED TEXT</Text> </Svg
<G>元素能夠把它內部的子元素組合成一個分組,G元素內全部的子元素都將繼承除(id,變形屬性以外的全部屬性)
下面兩段代是相同的
<Svg height="60" width="200" > <G width="100" height="30" fill="blue" x="0"> <Rect y="0" /> <Rect y="30" /> </G> </Svg> <Svg height="60" width="200" > <G> <Rect width="100" height="30" fill="blue" x="0" y="0" /> <Rect width="100" height="30" fill="blue" x="0" y="0" /> </G> </Svg>
G元素的變形屬性不會被繼承
<Svg height="100" width="200" > <G rotate="50" origin="100, 50" > <Line x1="60" y1="10" x2="140" y2="10" stroke="#060" /> <Rect x="60" y="20" height="50" width="80" stroke="#060" fill="#060" /> <Text x="100" y="75" stroke="#600" fill="#600" textAnchor="center" > Text grouped with shapes</Text> </G> </Svg>
<Defs>元素內的全部圖形都不會被繪製在畫布上,<Defs>元素用於定義一些代碼複用相關的元素,下面使用其餘元素解釋<Defs>元素的用法
使用<Use>元素能夠進行代碼複用,能夠將<Defs>元素中定義的元素複製一份
繪製在當前<Use>元素的位置
<Svg height="100" width="300" > <Defs> <G id="shape"> <Circle cx="50" cy="50" r="50" /> <Rect x="50" y="50" width="50" height="50" /> <Circle cx="50" cy="50" r="5" fill="blue" /> </G> </Defs> <Use href="#shape" x="20" y="0"/> <Use href="#shape" x="170"y="0" /> </Svg>
*注意:*Use元素使用href指定複用對象(SVG中是使用的xlink:href=),屬性值爲#
+須要複用元素的id,也能夠複用<Defs>外面具備id屬性的元素,Use元素上的屬性將覆蓋被複用元素的屬性
使用<Symbol>元素能夠更快捷的定義元素,使用<Defs>元素定義可複用元素時,每一個子元素對應一個可複用元素, 若是須要複用多個元素組成的圖形時一般使用<Symbol>元素進行定義,<Symbol>自身也不會被繪製在畫布上,並且能夠 給Symbol指定viewbox和preserveAspectRatio屬性,更爲方便的對圖形進行縮放
<Svg height="150" width="110" > <Symbol id="symbol" viewbox="0 0 150 110" width="100" height="50"> <Circle cx="50" cy="50" r="40" strokeWidth="8" stroke="red" fill="red"/> <Circle cx="90" cy="60" r="40" strokeWidth="8" stroke="green" fill="white"/> </Symbol> <Use href="#symbol" x="0" y="0" /> <Use href="#symbol" x="0" y="50" width="75" height="38" /> <Use href="#symbol" x="0" y="100" width="50" height="25" /> </Svg>
<LinearGradient>能夠定義一個線性漸變的填充規則,改元素必須定義在<Defs>元素內。
線性漸變能夠被定義爲水平漸變、垂直漸變和帶角度的漸變
經過調整x1,y1設置漸變開始點,x2,y2設置漸變結束點
使用<Stop>元素定義漸變的變色點
<Svg height="150" width="300" > <Defs> <LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0"> <Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" /> <Stop offset="1" stopColor="red" stopOpacity="1" /> </LinearGradient> </Defs> <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" /> </Svg>
注意:<LinearGradient>和<RadialGradient>的x1,y2,x2,y2屬性都可以使用百分比
下面<LinearGradient>繪製的結果等同於上面代碼的繪製結果
<LinearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%"> <Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" /> <Stop offset="100%" stopColor="red" stopOpacity="1" /> </LinearGradient>
<RadialGradient>能夠定義一個線性漸變的填充規則,和<LinearGradient>同樣改元素必須定義在<Defs>元素內。
cx,cu,rx,ry屬性定義了最外層漸變橢圓的位置座標和大小,fx和fy屬性定義了最內層漸變橢圓的位置座標
<Svg height="150" width="300" > <Defs> <RadialGradient id="grad" cx="150" cy="75" rx="85" ry="55" fx="150" fy="75"> <Stop offset="0" stopColor="#ff0" stopOpacity="1" /> <Stop offset="1" stopColor="#83a" stopOpacity="1" /> </RadialGradient> </Defs> <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" /> </Svg>
好了,差很少就這些了。
上面包含了大多數react-native-art-svg
類庫的用法,想看更具體的例子請fork以後運行Example下面的示例工程。
讓咱們再回到在文章開頭的那個項目中去,經過幾番折騰,終於把項目中的全部圖片都換成了矢量圖形,在iOS下再也沒有出現圖片閃動或圖片不能和界面同時渲染的問題了。
並且SVG代碼再通過一番優化以後,把200多K的圖片變成了30多K的矢量代碼。完美!
我再順帶講一下SVG代碼優化:
局中描邊
,由於只有局中描邊導出的SVG代碼會保留原始設計稿中的圖形信息,用外層描邊或內側描邊生成的SVG代碼都是經過<path>元素繪製的,這樣就會使得SVG代碼成倍的增長。