搞一个自个的React Native图标库

罗秦

共 4773字,需浏览 10分钟

 · 2021-08-06

想在React Native(以下称RN)使用图标,其实办法很多,想简单省事的话,直接梭哈 react-native-vector-icons 就可以,这个属于目前RN平台上最为热门的图标解决方案了,使用广泛也比较稳定,不足之处在于需要将字体文件打包进app,不能热更新。

35ef6340eff26d4fd5803008f3e1d26f.webp

这里不深究 react-native-vector-icons 的实现原理,来看看如果我们自己想做一个自己的图标库,应该怎么来做呢?


一、怎么显示一个图标?


要想展示一个图标,可以有几种方式:webfont、svg或者图片。


  1. webfont,网页上比较常见的图标库实现方式,需要将字体文件打包到App内部。

  2. svg,体积小,矢量图,拉伸不失真,且可以通过svg属性控制颜色。

  3. 图片,使用简单,但是无法动态设置颜色,且无法适应不同倍率的屏幕。


综合来说,webfont需要将字体文件打包到App内部,不能热更,svg本事是xml代码形式的,易于控制,相对来说svg比较适合做图标库。


二、用svg实现图标库的思路


对于图标库来说,需要满足以下两个基本条件:


  • 可以设置大小,任意大小不变形,svg本身就是矢量的,天生就支持。

  • 可以设置颜色,可以通过设置svg fill属性来实现。


那么,思路很简单,只需要开发者,提前做好svg图标。将svg文件解析出来,动态设置颜色,图标大小可以通过设置元素width/height来实现。


三、RN 加载svg图片


一般来说,我们会用 react-native-svg 来加载 svg。

yarn add react-native-svg

加载svg的方式有多种,可以加载远程svg,或者自己编写svg代码均可。

// 使用远程svg图片<SvgUriwidth="100%"height="100%"uri="http://thenewcode.com/assets/images/thumbnails/homer-simpson.svg"  />
// 自行编写svg代码<Svg height="50%" width="50%" viewBox="0 0 100 100"><Circlecx="50"cy="50"r="45"stroke="blue"strokeWidth="2.5"fill="green"  /><Rectx="15"y="15"width="70"height="70"stroke="red"strokeWidth="2"fill="yellow"  /></Svg>

当然也可以加载本地的svg图片,需要配置metro config和babel,有兴趣可以前往文档进行阅读。


三、解析svg文件


解析svg文件可以通过 react-native-svg-uri 来实现。

npm install react-native-svg-uri --save
<SvgUri width="200" height="200" source={require('./img/homer.svg')} />

在react-native中默认只能require png和xml文件,需要require svg文件,需要做额外的配置。


此外,频繁的require本地文件也会减慢速度,我们可以读取svg文件,解析xml内容,仅保留path标签,其余部分内容对我们显示图片来说是无用的。


我们可以编写一个脚本,批量读取svg文件,解析xml数据,react-native-svg-uri也预留了使用xmlData的接口。

<SvgUri width="200" height="200" svgXmlData={svgXmlData} fill="red" /


react-native-svg-uri 渲染svg图片依赖于 react-native-svg,但是 react-native-svg-uri 这个库已经很久不更新了,其依赖的 react-native-svg 版本过低,会导致实际使用过程,因为原生和react-native-svg-uri 使用的react-native-svg版本不同而报错。


react-native-svg-uri 常年不更新,且代码简单,建议直接将仓库代码扒下来放到自己的工具代码中使用,ts版本也可以从作者仓库里面复制一份,代码可以在 src/lib/react-native-svg-uri 找到,仓库地址可以点击下方查看原文获取。


四、具体步骤


仔细串一串svg图标渲染的整个流程:svg文件->脚本解析->svg xml data->react-native-svg-uri渲染svg图片。


1、准备svg素材


svg素材可以由设计师提供,也可以自行前往iconfont下载,或者自己制作。


3ce0fd72eba2789553a07cbade959c06.webp


2、脚本处理


require大量静态资源影响性能,且需要做额外的配置,为了方便处理,这里我们将所有的svg文件简单处理一下,转换成一个js文件。这里我使用nodejs编写,可以选自己喜欢的脚本语言处理一下。

const path = require('path');const fs = require('fs');
const svgFileDir = path.resolve(__dirname, '../../src/assets/svg');
function readSvgFile(svgFileName) { return new Promise((resolve, reject) => { fs.readFile(path.join(svgFileDir, svgFileName), 'utf8', (error, svgFile) => { let svgPath = svgFile.replace(/<\?xml.*?\?>|<\!--.*?-->|<!DOCTYPE.*?>/g, ''); svgPath = svgPath.replace(/[\r\n]*/g, ''); if (error) { return reject(error); } if (svgFileName.indexOf('.svg') === -1) { return resolve({}); } resolve({ [svgFileName.slice(0, svgFileName.lastIndexOf('.'))]: svgPath, }); }); });}
function readSvgDir() { return new Promise((resolve, reject) => { fs.readdir(svgFileDir, (error, svgFiles) => { if (error) { return reject(error); } Promise.all(svgFiles.map(svgFileName => readSvgFile(svgFileName))) .then(data => resolve(data)) .catch(err => reject(err)); }); });}
readSvgDir() .then(data => { const svgFile = `export default { ${data.filter(item => Object.keys(item)[0]).map(item => `'${Object.keys(item)[0]}': \`${Object.values(item)[0]}\``)}}\n`;
fs.writeFile(path.resolve(svgFileDir, 'index.js'), svgFile, err => { if (err) { throw new Error(err); } }); }) .catch(error => { throw new Error(error);  });

处理的产物,类似于这样。

export default {  'arrow-down': `svg data`,  'arrow-up': `svg data`,  ... }


3、封装Icon Compoment


上面两步已经准备好了所有素材,只需要将对应的svg xml data配合size以及color做渲染即可。

import React from 'react';import { Pressable, StyleProp, ViewStyle } from 'react-native';import SvgUri from '../../lib/react-native-svg-uri';import svgs from '../../assets/svg';
export type IconProps = { // icon名字 name: string; // 颜色 color?: string; // 大小 size?: number; // 点击回调 onPress?: () => void; // style style?: StyleProp<ViewStyle>;};
const Navbar: React.FC<IconProps> = props => { const { name, color = '#666', size = 20, onPress, style = {} } = props; let svgXmlData = (svgs as any)[name];
if (!svgXmlData) { throw new Error(`No Icon Named ${name} Was Found!`); } return ( <Pressable onPress={onPress} style={style}> <SvgUri width={size} height={size} svgXmlData={svgXmlData} fill={color} /> </Pressable> );};
export default Navbar;


使用方式

<Icon name="arrow-down" color="red" />


处理到这里,基本已经完成了,需要新增图标时,只需要将svg文件放到设定好的文件夹中,然后再执行svg的处理脚本即可,为了方便使用,可以添加一个npm script脚本。

{  "scripts": {    "build:svg": "node scripts/svg/index.js"  }}

以上所有代码,均可在 https://github.com/AspenLuoQiang/react-native-ui-view 仓库中找到,或点击下方查看原文进入代码仓库。


浏览 48
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报