RN TSX基于react-native-vector-icons和iconfont.cn 的生成自定义Icon组件的工具

基于react-native-vector-icons与 iconfont 的生成自定义Icon组件的工具

前言

原本项目中使用了 react-native-vector-icons 中已有的图标,但是领导希望应用内的 icon与UI同学的保持一致,所以需要使用自定义icon的能力,但是每次UI同学更新完icon库之后,都要手动去维护本地的Iocn的名字比较麻烦,所以产生了写一个脚本自动修改本地图标的想法。
首先我们的图标是维护在阿里巴巴矢量图标库上的;

阿里巴巴矢量图标库链接https://www.iconfont.cn

准备工作

项目导入依赖 react-native-vector-icons
从 阿里巴巴矢量图标库下载iconfont的压缩包并解压,下载方式如下图:
请添加图片描述

使用

将末尾的代码放在项目的根目录下,然后执行下面命令即可生成
注意: iOS 需要将写到 ios的文件在Xcode中添加到项目中

命令:node task-iconfont.js component=路径 iconNames=路径 androidFolder=路径 iOSFolder=路径 fileName=文件名 ./src/…解压后路径
参数说明:
component:(可选参数)自定义icon组件的路径 ,默认:src/icon-out/IconSet.tsx
iconNames:(可选参数)全部icon名称的路径,默认:无不输出
androidFolder:(可选参数)android 源文件存放位置;默认:android/app/src/main/assets/fonts/
iOSFolder:(可选参数)ios 源文件存放位置;默认:ios/fonts/
fileName: (可选参数)源文件的文件名(默认为读取到的源文件的文件名)
./src/…解压后路径: (必填参数)下载回来的压缩包解压后的文件夹

task-iconfont.js 实现

const fs = require('fs');
const childProcess = require('child_process');
const path = require('path');
const chalk = require('chalk');
const util = require('util');

const defaultConfig = {
  component: 'src/icon-out/IconSet.tsx',
  iconNames: undefined,
  androidFolder: 'android/app/src/main/assets/fonts/',
  iOSFolder: 'ios/fonts/',
  fileName: undefined,
};

const run = () => {
  const argv = process.argv;
  const [, , ...params] = argv;
  if (!params) {
    Logger.error('参数错误!');
  }
  const config = initializeConfig(params);
  if (config) {
    const json = JSON.parse(fs.readFileSync(config.res.json, { encoding: 'utf8', mode: 438 }));
    writeIconSet(config.component, json, config.fileName);
    const ttf = fs.readFileSync(config.res.ttf);
    writeTtf(config.androidFolder, config.fileName, ttf);
    writeTtf(config.iOSFolder, config.fileName, ttf);
    if (config.iconNames) {
      writeIconNames(config.iconNames, json, config.component);
    }
  }
};

/**
 *
 * @param {string} ttf
 * @param {string[]} params
 * @returns {{
 * res:{ttf:string;json:string};
 * component: string;
 * iconNames:string;
 * androidFolder:string;
 * iOSFolder:string;
 * fileName: string
 * }}
 */
const initializeConfig = (params) => {
  const result = {
    ...defaultConfig,
  };
  for (let index = 0, len = params.length; index < len; index++) {
    const str = params[index].trim();
    if (str.indexOf('=') !== -1) {
      const [key, value] = str.split('=');
      result[key] = value;
    } else {
      try {
        const stat = fs.statSync(str, { encoding: 'utf8' });
        if (stat.isDirectory()) {
          const ttf = findFileBySuffix(str, '.ttf');
          const json = findFileBySuffix(path.dirname(ttf), '.json');
          if (ttf && json) {
            result.res = { ttf, json };
          }
        } else {
          Logger.error('未知参数: ' + str);
          return undefined;
        }
      } catch (error) {
        Logger.error('未知参数: ' + str);
        return undefined;
      }
    }
  }
  if (!result.res) {
    Logger.error('文件自动查找失败;请确认文件夹内是否存在 *.json 和 *.ttf 文件!');
    return undefined;
  }
  if (!result.fileName) {
    result.fileName = path.basename(result.res.ttf);
  }
  return result;
};
/**
 *  遍历查找 ttf文件
 * @param {string} folder 要查询的文件夹
 * @param {string} extname 后缀
 * @returns {string|undefined}
 */
const findFileBySuffix = (folder, extname) => {
  const files = fs.readdirSync(folder, { encoding: 'utf8' });
  for (let index = 0, len = files.length; index < len; index++) {
    const currFile = path.join(folder, files[index]);
    const stat = fs.statSync(currFile, { encoding: 'utf8' });
    if (stat.isDirectory()) {
      const find = findFileBySuffix(currFile, extname);
      if (find) {
        return find;
      }
    } else {
      if (path.extname(currFile).toLocaleLowerCase() === extname) {
        return currFile;
      }
    }
  }
  return undefined;
};
/**
 * 写入组件文件
 * @param {string} outFile 输出文件路径
 * @param {{font_family:string;glyphs:{font_class:string; unicode_decimal:string}}} json 数据源
 * @param {string} name 文件名称
 */
const writeIconSet = (outFile, json, name) => {
  const folder = path.dirname(outFile);
  if (!fs.existsSync(folder)) {
    fs.mkdirSync(folder);
  }
  const option = { encoding: 'utf8', mode: 438 };
  const header = `import { createIconSet } from 'react-native-vector-icons';`;
  const footer = ` 
  const Icon = createIconSet(glyphMap, '${json.font_family}', '${name}');
  export default Icon;
  export type IconName = keyof typeof glyphMap;
  `;
  const glyphMap = {};
  for (let i = 0, len = json.glyphs.length; i < len; i++) {
    const { font_class, unicode_decimal } = json.glyphs[i];
    glyphMap[font_class] = unicode_decimal;
  }
  const body = `const glyphMap=${JSON.stringify(glyphMap)};`;
  fs.writeFile(outFile, `${header}${body}${footer}`, option, () => {
    // && git add  ${outFile}
    childProcess.exec(`eslint --fix ${outFile}`, (error) => {
      if (error) {
        Logger.warn(error);
      } else {
        Logger.info(`${outFile}  写入成功!`);
      }
    });
  });
};
const writeIconNames = (outFile, json, iconset) => {
  const folder = path.dirname(outFile);
  if (!fs.existsSync(folder)) {
    fs.mkdirSync(folder);
  }
  const option = { encoding: 'utf8', mode: 438 };
  let relative = removeExtname(path.relative(path.dirname(outFile), iconset));
  relative = relative.startsWith('..') ? relative : `.${path.sep}${relative}`;
  const header = `import {IconName} from '${relative}';`;
  const footer = ` 
  export default icons;
  `;
  const icons = [];
  for (let i = 0, len = json.glyphs.length; i < len; i++) {
    const { font_class } = json.glyphs[i];
    icons.push(font_class);
  }
  const body = `const icons:IconName[]=${JSON.stringify(icons)};`;
  fs.writeFile(outFile, `${header}${body}${footer}`, option, () => {
    childProcess.exec(`eslint --fix ${outFile}`, (error) => {
      if (error) {
        Logger.warn(error);
      } else {
        Logger.info(`${outFile}  写入成功!`);
      }
    });
  });
};
/**
 * 写入文件
 * @param {string} outFolder 路径
 * @param {string} fileName 文件名称
 * @param {Buffer} data 数据
 */
const writeTtf = (outFolder, fileName, data) => {
  if (outFolder) {
    if (!fs.existsSync(outFolder)) {
      fs.mkdirSync(path.dirname(outFolder));
    }
    const outFile = path.join(outFolder, fileName);
    fs.writeFile(outFile, data, (err) => {
      if (err) {
        Logger.warn(err);
        return;
      }
      childProcess.exec(`eslint --fix ${outFile}`, (error) => {
        if (error) {
          Logger.warn(error);
        } else {
          Logger.info(`${outFile}  写入成功!`);
        }
      });
    });
  }
};
/**
 *
 * @param {string} file 文件路径
 * @returns 去掉后缀之后的路径
 */
const removeExtname = (file) => {
  return file.substring(0, file.length - path.extname(file).length);
};
/**
 * 输出日志 工具类
 */
const Logger = {
  error: (message, ...optionalParams) => {
    const format = util.format.call(this, message, ...optionalParams);
    process.stdout.write(chalk.red(format + '\n'));
  },
  warn: (message, ...optionalParams) => {
    const format = util.format.call(this, message, ...optionalParams);
    process.stdout.write(chalk.yellow(format + '\n'));
  },
  info: (message, ...optionalParams) => {
    const format = util.format.call(this, message, ...optionalParams);
    process.stdout.write(chalk.green(format + '\n'));
  },
};
run();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Native中可以使用第三方库`react-native-scrollable-tab-view`来实现滑动标题栏。以下是一个简单的示例代码: ```tsx import * as React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import ScrollableTabView, { DefaultTabBar } from 'react-native-scrollable-tab-view'; type Props = {}; const FirstTabScreen = () => { return ( <View style={styles.container}> <Text>First Tab</Text> </View> ); }; const SecondTabScreen = () => { return ( <View style={styles.container}> <Text>Second Tab</Text> </View> ); }; const ScrollableTabNavigator = (props: Props) => { return ( <ScrollableTabView renderTabBar={() => <DefaultTabBar />} tabBarUnderlineStyle={styles.tabBarUnderline} tabBarBackgroundColor="#FFFFFF" tabBarActiveTextColor="#000000" tabBarInactiveTextColor="#999999" > <FirstTabScreen tabLabel="Tab 1" /> <SecondTabScreen tabLabel="Tab 2" /> </ScrollableTabView> ); }; export default ScrollableTabNavigator; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, tabBarUnderline: { backgroundColor: '#000000', height: 2, }, }); ``` 在这个示例中,我们首先导入了`react-native-scrollable-tab-view`库中的`ScrollableTabView`和`DefaultTabBar`组件。然后,我们创建了两个Tab项组件`FirstTabScreen`和`SecondTabScreen`,每个组件中都有一个`tabLabel`属性,用于设置Tab的标签。最后,我们使用`ScrollableTabView`组件来包含Tab项,并设置了一些Tab导航的样式属性,例如`tabBarUnderlineStyle`、`tabBarBackgroundColor`、`tabBarActiveTextColor`和`tabBarInactiveTextColor`。在`renderTabBar`属性中,我们使用了`DefaultTabBar`组件来渲染Tab导航栏。 注意:`react-native-scrollable-tab-view`库已经很久没有更新了,建议使用`react-native-tab-view`等其他替代库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值