[轉] 在React Native中使用ART

來源:互聯網
上載者:User

標籤:

http://bbs.reactnative.cn/topic/306/%E5%9C%A8react-native%E4%B8%AD%E4%BD%BF%E7%94%A8art

前半個月搗騰了一下React Native ART
現在手上閑下來了和大家分享一下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 Native中使用 ART?

我在前端頁面切片的時候比較喜歡用SVG,有條件我都盡量讓設計師出AI的設計稿或者把表徵圖導成SVG的
因為SVG和圖片相比,優點太多太多:可以代碼複用,可以無損放大,通過合理匯出的svg代碼比同等壓縮過的圖片檔案大小要小很多,而且svg代碼還可以通過gzip壓縮
最近在做一個原生內嵌的React Native項目,設計師是按照原生APP的標準把每個表徵圖切了3個尺寸給我,感謝React Native中強大的打包工具,可以很方便的使用這寫表徵圖。所以項目開始也沒想著要用SVG(當時android的ART還沒開源,這也是一個重要因素)。
在整個項目完成之後,看了一下,所有圖片在經過壓縮之後加起來有200多K,大小還可以接受。
但是在項目後期最佳化過程中,發現一個使用圖片表徵圖非常致命的問題:

<Image />元素在iOS下不是同步渲染的,即使是使用的本地圖片資源!
更過分的是具有相同source的多個Image也不能在同一幀裡面渲染出來的,如下的5個表徵圖,在iOS下它們不會同時被渲染出來,這樣造成的影響是在開啟一個列表的時候,看見這些表徵圖一個接一個無序的跳出來,這個效果如果在iphone4s,5s下看相當的明顯,很噁心,完全不能忍受!想過各種方法也沒辦法繞過去。

首屏的如下幾個表徵圖以及返回按鈕和logo,還有TabBar裡面的表徵圖在iOS下都是在介面出現的若干幀之後再無序的跳出來,這樣的效果很不好,一點都沒有原生APP的感覺,有一種hybird的既視感。

後來等到了0.18.0,Android的ART對外開源了,我想到為什麼不用SVG表徵圖嘗試一下呢?
然後讓設計師把這個表徵圖:

重新切成SVG給我,然後再把SVG轉成ART。
在iOS下完美渲染!所有表徵圖和介面文字一起出現,不管是放5個還是100個。而且對介面的整體渲染速度也沒有任何能感知到的影響。
在安卓下也是同樣的完美,而且產生的ART代碼要比圖片小了很多。

<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.69C50,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.52c-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.52c-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.55l4.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.9C45.53,21.06,45.27,22.16,44.5,22.92z`}        />    </Group></Surface>

這裡面的路徑代碼是通過PS CC直接匯出的,沒有做任何最佳化,如果再最佳化一番可以再縮小一半。SVG代碼最佳化後面再做介紹。
下面正式進入正題

如何在React Native中使用 ART?安裝 ART

安裝ART很簡單
安卓裡面根本不用安裝,0.18.0裡面內建,直接在js引入既可

import React, {ART} from ‘react-native‘;const {Surface, Group, Shape} = ART;

iOS裡面的ART是可選的,你需要手動匯入xcodeproj檔案,以及加入靜態連結庫
但是也很簡單:教程

使用ART

請大家原諒我,其實我是標題黨,我不會在這裡詳細的講解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.69C50,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.52c-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.52c-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.55l4.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.9C45.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內部
並且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

<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

<Circle>元素可以在畫布上繪製一個圓形,圓型需要cx,cy,r三個屬性,分別對應圓形x軸座標,y軸座標以及半徑

<Svg    height="100"    width="110">    <Circle        cx="50"        cy="50"        r="50"        fill="pink"    /></Svg>

橢圓:Ellipse

<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

<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

<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

<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

<Path>的d屬性內定義了一系列的路徑座標以及繪製規則命令,上面的所有圖形都可以通過Path繪製而成

  • M = 把繪製點移動到某個位置
  • L = 從當前繪製點畫一條直線到某個座標
  • H = 從當前繪製點沿著x軸水平畫線
  • V = 從當前繪製點沿著y軸垂直畫線
  • C = 從當前繪製點畫一條曲線到某個座標
  • S = 從當前繪製點畫一條平滑的曲線到某個座標
  • Q = 從當前繪製點畫一條貝賽爾曲線到某個座標
  • T = 從當前繪製點畫一條平滑的貝賽爾曲線到某個座標
  • A = 從當前繪製點畫一條橢圓曲線到某個點
  • Z = 閉合當前路徑

注意: 上面所有的命令都可以使用小寫字母,大寫字母的命令是使用的座標是絕對值,小寫字母的命令的座標是相對值

<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

<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

SVG進階元素使用分組:G

<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>元素用於定義一些代碼複用相關的元素,下面使用其他元素解釋<Defs>元素的用法

複用:Use

使用<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

使用<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:線性漸層

<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:放射狀漸層

<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代碼最佳化:

    1. 優先使用基礎圖形!
      優先使用<Rect><Circle><Line><Polygon><Polyline><Ellipse>之類的基礎圖形
      能夠使用這些元素繪製的圖形就盡量使用這些元素來繪製,因為同樣一個圖形使用<Path>命令產生的程式碼要比其他基礎元素產生的程式碼多很多倍
    2. 讓設計師在畫圖的時候全部使用局中描邊,因為只有局中描邊匯出的SVG代碼會保留原始設計稿中的圖形資訊,用外層描邊或內側描邊產生的SVG代碼都是通過<path>元素繪製的,這樣就會使得SVG代碼成倍的增加。

[轉] 在React Native中使用ART

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.