美文网首页eiYoElectron/web Native 研究小组
react-native icon解决方案(svg)

react-native icon解决方案(svg)

作者: cloudZqy | 来源:发表于2017-03-24 19:35 被阅读8774次

** 在开发app的过程中总是少不了各种各样的icon图标。移动端和pc端的解决方式各有不同,而RN与之前的开发方式都有所不同,所以我们要对各种引入图标的方式进行权衡。**

一: 三种方式

目前主流的解决图标的方式又三种,如下:
1.图片
使用png图片,应该是移动端最普适的方案,对RN来说,使用图片解决图标最简单,也最复杂,简单的是RN自己就能够解析图片,因此不用引入任何外部库,复杂的就是为了ios和安卓的各种屏幕,我们可能要对每个图片准备各种尺寸。
2. IconFont
对于web开发来说,字体图标绝对是解决icon最熟悉的方案了。也由此,react-native的开源库react-native-vector-icons开始流行起来。这种方案解决简单,只用引进这个库和.ttf文件,就能像写web一样使用字体图标了。并且现在很多demo都是用字体图标来解决的。
3.svg
之所以要把svg和图片分开,就是因为RN是默认不支持svg的,我们需要引入react-native-svg这个库才能渲染svg图标。svg对比图片拥有体积小,而且因其可缩放特性,不用理会用户屏幕的尺寸。

二:对比

类型 优势 劣势
图片(打包) 使用方便,直接用require和Image标签就可以使用 bundle体积增大,特别是热更新对流量,影响太大。需要根据屏幕不同准备多种尺寸。
图片(URI) 同上,更换方便,远程管理 基本同上,缓存管理比较麻烦,需要另外的库。
IconFont 随app打包,文件小,使用便利,不用担心屏幕屏幕尺寸 不能热更新,需要引入额外的库
svg(打包) 文件极小,可随bundle热更新,可缩放图形,不用担心屏幕尺寸问题 需要引入额外的库
svg(URI) 基本同图片,不用担心屏幕尺寸 缓存

三:决定实施方案 (svg)

鉴于使用图片为了防止模糊,要准备多倍图,首先就被pass掉了。而字体图标做为我常用的手段,特别是公司的字体是通过icomoo这种网站统一管理的,本来是很倾向于使用的,奈何.ttf文件必须随项目打包到app里面,不能热更新。至少在没有放弃codepush的情况下,只能放弃了。接下来就只有使用svg了 。

svg的体积极小,几十个图标文件加起来不到3k,随bundle打包是最好的选择,正好现在的字体图标管理网站也能生成svg文件,很方便和设计师合作。设计师只用将需要使用的svg图标上传到icomoo上命名好,然后打包下载就能使用。

使用react-native-svg就能对svg的标签解析成图片,而使用react-native-svg-uri则能把svg文件的xml解析成响应的component。这样就能把svg文件转化成图形。但是后来发现这在安卓中行不通,因为安卓的RN项目在release打包后(非debug模式),只能允许require pngxml格式的文件。不过这并不是什么大问题,本来对icomoo生成svg文件中,我们仅仅需要path标签,其余的都是浪费空间的,而且频繁require静态文件也会减慢速度。我们可以用脚本来将svg文件批量生成js使用的字符串,然后通过react-native-svg-uri来解析xml。这个库作者也考虑到android的问题预留了接受字符串的api。


于是我们的使用方式变成了:svg文件->js的xml数据集合->Svg Component。
另外在react-native-svg-uri更新太慢,其npm包依赖了低版本的react-native-svg。如果你使用的5.0版本以上的svg,会由于原生和react-native-svg-uri所使用的react-native-svg版本不同而报错。其实这个库原理很简单,而且只有两百行代码,很好维护。建议不通过npm直接在项目中使用,可以解决版本问题。

四:具体步骤

步骤1: 下载svg文件


icomoo仓库

以我使用的icomoo为例,打包下载下来的SVG文件夹如下


QQ20170324-190940@2x.png

步骤2:脚本处理
每次进app请求多个svg很浪费资源,并且安卓本身就不支持svg静态文件的require,所以我们需要用简单的脚本处理一下,把多个svg的字符合并到一个js对象中,代码如下,运行下面的脚本 node getSvg。这里我时用node写的,当然你也可以用自己习惯的脚本语言来处理。

//  getSvg.js
var fs = require('fs');
var path = require('path');
const svgDir = path.resolve(__dirname, './svgs');

// 读取单个文件
function readfile(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(path.join(svgDir, filename), 'utf8', function(err, data) {
      console.log(data.replace(/<\?xml.*?\?>|<\!--.*?-->|<!DOCTYPE.*?>/g, ''));
      if (err) reject(err);
      resolve({
        [filename.slice(0, filename.lastIndexOf('.'))]: data,
      });
    });
  });
}

// 读取SVG文件夹下所有svg
function readSvgs() {
  return new Promise((resolve, reject) => {
   fs.readdir(svgDir, function(err, files) {
     if (err) reject(err);
     Promise.all(files.map(filename => readfile(filename)))
      .then(data => resolve(data))
      .catch(err => reject(err));
   });
  });
}

// 生成js文件
readSvgs().then(data => {
  let svgFile = 'export default ' + JSON.stringify(Object.assign.apply(this, data));
  fs.writeFile(path.resolve(__dirname, './svgs.js'), svgFile, function(err) {
    if(err) throw new Error(err);
  })
}).catch(err => {
    throw new Error(err);
  });

这样生成了一个svgs.js文件。其结构是

// svgs.js
export default {
  'svgName1': 'xmlData1...',
  'svgName2': 'xmlData2...',
  ...
}

步骤3:封装Svg Component

// Svg.js
import React, { Component } from 'react';
import {
  ViewStyle,
} from 'react-native'
import SvgUri from '../../lib/react-native-svg-uri';
import svgs from '../../assets/svgs';

export default class Svg extends Component<SvgProperties, void>{
  render() {
    const {
      iocn,
      color,
      size,
      style,
    } = this.props;
    let svgXmlData = svgs[this.props.icon];

    if (!svgXmlData) {
      let err_msg = `没有"${this.props.icon}"这个icon,请下载最新的icomoo并 npm run build-js`;
      throw new Error(err_msg);
    }
    return (
      <SvgUri
        width={size}
        height={size}
        svgXmlData={svgXmlData}
        fill={color}
        style={style}
      />
    )
  }
}

使用

render() {
  return <Svg icon="ac_unit" size="40" fill="#ccc"/>
}
svg使用

新加了一个demo https://github.com/cloudZQY/react-native-svg-demo
$ git clone git@github.com:cloudZQY/react-native-svg-demo.git
$ cd react-native-svg-demo
$ npm i
$ npm run build-svg
$ react-native run-android or $ react-native run-ios

相关文章

网友评论

  • fangkyi03:你这个无法识别有css样式的svg图标
    <style type="text/css">
    .st0{opacity:0.23;fill:#55D80F;enable-background:new ;}
    .st1{fill:url(#SVGID_1_);}
    .st2{fill:#FFFFFF;}
    .st3{fill:url(#SVGID_2_);}
    .st4{fill:url(#SVGID_3_);}
    .st5{fill:url(#SVGID_4_);}
    </style>
    美工导出的svg里面有这种样式的 用浏览器什么都能解析 但是用rn的就是解析不了
    fangkyi03:@cloudZqy react-native svg uri也是不支持的
    cloudZqy:跟css样式不兼容很正常,需要用纯粹的svg。做这个最好自己写些小脚本解决
  • b5b2e10ccc0e:为什么我按照 demo 里的写法 Sketch 导出的 svg 图片 无法修改颜色啊?
    b5b2e10ccc0e:@cloudZqy 在这里打印color 信息没有填进去
    cloudZqy:@b5b2e10ccc0e 填充的颜色是由svg标签的fill属性控制的,请看react-native-svg-uri的第164行代码
    [nodeName]: this.props.fill && nodeName === 'fill' ? this.props.fill : nodeValue
    如果断点到这行看看结果,有没有将color填入进去。
    b5b2e10ccc0e:<?xml version="1.0" encoding="UTF-8"?>
    <svg width="8px" height="12px" viewBox="0 0 8 12" version="1.1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
    <!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
    <title>Shape</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="02-Tenant-Register" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="02-Company" transform="translate(-327.000000, -157.000000)" fill="#D82D42">
    <g id="Group" transform="translate(25.000000, 132.000000)">
    <g id="chevron-right" transform="translate(302.000000, 25.000000)">
    <polygon id="Shape" points="1.4 0 1.77635684e-15 1.4 4.6 6 1.77635684e-15 10.6 1.4 12 7.4 6"></polygon>
    </g>
    </g>
    </g>
    </g>
    </svg>
  • developr:我一直出现版本问题,还有react-native版本问题,你有项目地址吗,我想对比一下看看错在哪里
    cloudZqy:文章最后有github地址,不过比较老了,RN更新比较快。最近没有写RN,所以没有更新过。
  • f36351fa711e:Invariant Violation: Native component for "RNSVGSvgView" does not exist
    想用你这个方案,但这个问题始终无法解决...
    9e2bb9a197c5:@树_a028 怎么解决的
    f36351fa711e:最后还是解决了这个问题,你这个方案太完美了!:+1:
  • 我是滕先生:为什么UI给的带多种颜色的SVG,导入后只显示一种了,默认是纯黑色,能显示原色嘛
    我是滕先生:@Cindy孙迪 我发现是因为一些svg图导的问题,具体原因我也不清楚,你可以用 affinity designer 重新导一下。
    Cindy孙迪:@cloudZqy 删除 SvgUri 里的 fill 还是黑色的啊
    cloudZqy:SvgUri的fill的props不传的时候就是原色
  • 万象归真:楼主你好,请问:svgs.js需要通过getSvg.js生成,那么怎么执行相关的生成js文件代码呢?我按照你的方法做,没有生成svgs.js文件,导致报错。
    cloudZqy:getSvg.js是用node扫描./svgs的所有svg文件,合成一个./svgs.js 文件。当前目录执行 node getSvg
  • 桃之_夭夭_:大神,请问我这边使用你的代码后报No ViewManager defined for class RNSVGPath怎么解决啊
    桃之_夭夭_:发现将svg通过iconMoon转成字体文件也挺方便的:smile:
    桃之_夭夭_:@cloudZqy 不行哎,我把版本号改成你的demo里的版本号了,运行报错,是react-native-svg使用的react-native里面的东西引用不到,
    UnableToResolveError: Unable to resolve module `react-native/Libraries/Renderer/shims/createReactNativeComponentClass` from `E:\web\meican\node_modules\react-native-svg\elements\Rect.js`: Module does not exist in the module map or in these directories:
    cloudZqy:应该是版本升级导致的问题。请把package.json的react-native-svg和react-native 的版本号改成=xx.xx.xx,然后npm i试试。
  • 3ac545198ae8:简单看了下,重点在于如何将SVG转成RN支持的XML格式(这点我个人不懂),但是你的代码明显有问题,Object.keys获取的是key不是内容,然后很多数据是undefined。。。求大佬更新代码并给出SVG转成XML格式原理
    cloudZqy:只需要SvgUri组件的svgXmlData接受到相应的svg的字符串就行了。
    svg本来就是xml格式,至于你是复制粘贴,还是用脚本处理,都不影响。
  • Sleet:不过我运行代码˙各种报错呀
  • 菜鸟秦伟康:所以我们要处理一下,其中处理svg的脚本如下,我将其与typescript编译集成到了gulp的编译任务中。这句话是什么意思呀?大哥
    cloudZqy:@V_935e 最近项目上线,有点忙,你可以试一下我改的demo,如果运行有什么问题可以私信我
    菜鸟秦伟康:@cloudZqy 谢谢哥能回答我,就是你这个方案有没有gitub上的demo?本人新手确实有点笨,:flushed:
    cloudZqy:是我的表述有问题。运行一下node脚本处理就行了。
    `我将其与typescript编译集成到了gulp的编译任务中`,这句话只是为了提示将这件工作引入自动化构建流程而已,因为并不是必要的事,所以没有详解。现在发现会引起误解,所以已修改。
  • 1423f9a80aa5:扑通dalao

本文标题:react-native icon解决方案(svg)

本文链接:https://www.haomeiwen.com/subject/rlqrnttx.html