react学习之导入svg文件

react学习之导入svg文件

使用的是icofont图标库的文件,直接下载目标图标的svg,存放在svgs文件夹下.svgs文件夹
参考大佬的方法用node处理成键值对:

//导入node的文件模块
var fs = require('fs');
var path = require('path');
//定义想要处理的svg文件夹路径
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));
        });
    });
}

// 在当前的目录下生成svgs.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);
});

再处理下载的rn-svg-uri文件:需要yarn react-native-svg, 存放在svguri,js和svgutils.js文件夹下

import React, { Component } from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import xmldom from 'xmldom';
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';

import Svg, {
    Circle,
    Ellipse,
    G,
    LinearGradient,
    RadialGradient,
    Line,
    Path,
    Polygon,
    Polyline,
    Rect,
    Text,
    TSpan,
    Defs,
    Stop,
} from 'react-native-svg';

import * as utils from './svgUriUtils';

const ACCEPTED_SVG_ELEMENTS = [
    'svg',
    'g',
    'circle',
    'path',
    'rect',
    'defs',
    'line',
    'linearGradient',
    'radialGradient',
    'stop',
    'ellipse',
    'polygon',
    'polyline',
    'text',
    'tspan',
];

// Attributes from SVG elements that are mapped directly.
const SVG_ATTS = ['viewBox', 'width', 'height'];
const G_ATTS = ['id'];

const CIRCLE_ATTS = ['cx', 'cy', 'r'];
const PATH_ATTS = ['d'];
const RECT_ATTS = ['width', 'height'];
const LINE_ATTS = ['x1', 'y1', 'x2', 'y2'];
const LINEARG_ATTS = LINE_ATTS.concat(['id', 'gradientUnits']);
const RADIALG_ATTS = CIRCLE_ATTS.concat(['id', 'gradientUnits']);
const STOP_ATTS = ['offset'];
const ELLIPSE_ATTS = ['cx', 'cy', 'rx', 'ry'];

const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight', 'textAnchor'];

const POLYGON_ATTS = ['points'];
const POLYLINE_ATTS = ['points'];

const COMMON_ATTS = [
    'fill',
    'fillOpacity',
    'stroke',
    'strokeWidth',
    'strokeOpacity',
    'opacity',
    'strokeLinecap',
    'strokeLinejoin',
    'strokeDasharray',
    'strokeDashoffset',
    'x',
    'y',
    'rotate',
    'scale',
    'origin',
    'originX',
    'originY',
    'transform',
    'clipPath',
];

let ind = 0;

function fixYPosition(y, node) {
    if (node.attributes) {
        const fontSizeAttr = Object.keys(node.attributes).find(
            (a) => node.attributes[a].name === 'font-size',
        );
        if (fontSizeAttr) {
            return (
                '' + (parseFloat(y) - parseFloat(node.attributes[fontSizeAttr].value))
            );
        }
    }
    if (!node.parentNode) {
        return y;
    }
    return fixYPosition(y, node.parentNode);
}

class SvgUri extends Component {
    state = {
        fill: this.props.fill,
        svgXmlData: this.props.svgXmlData,
        createSVGElement: this.createSVGElement.bind(this),
        obtainComponentAtts: this.obtainComponentAtts.bind(this),
        inspectNode: this.inspectNode.bind(this),
        fetchSVGData: this.fetchSVGData.bind(this),
        isComponentMounted: false,
        // Gets the image data from an URL or a static file
    };

    componentDidMount() {
        if (this.props.source) {
            console.log(this.props);
            const source = resolveAssetSource(this.props.source) || {};
            this.fetchSVGData(source.uri);
        }
        this.setState({
            isComponentMounted: true,
        });
        // this.isComponentMounted = true;
    }
    UNSAFE_componentWillReceiveProps(nextProps) {
        if (nextProps.source) {
            const source = resolveAssetSource(nextProps.source) || {};
            const oldSource = resolveAssetSource(this.props.source) || {};
            if (source.uri !== oldSource.uri) {
                this.fetchSVGData(source.uri);
            }
        }

        if (nextProps.svgXmlData !== this.props.svgXmlData) {
            this.setState({ svgXmlData: nextProps.svgXmlData });
        }

        if (nextProps.fill !== this.props.fill) {
            this.setState({ fill: nextProps.fill });
        }
    }

    componentWillUnmount() {
        this.isComponentMounted = false;
    }

    async fetchSVGData(uri) {
        let responseXML = null,
            error = null;
        try {
            const response = await fetch(uri);
            responseXML = await response.text();
        } catch (e) {
            error = e;
            console.error('ERROR SVG', e);
        } finally {
            if (this.isComponentMounted) {
                this.setState({ svgXmlData: responseXML }, () => {
                    const { onLoad } = this.props;
                    if (onLoad && !error) {
                        onLoad();
                    }
                });
            }
        }

        return responseXML;
    }

    // Remove empty strings from children array
    trimElementChilden(children) {
        for (child of children) {
            if (typeof child === 'string') {
                if (child.trim().length === 0)
                    children.splice(children.indexOf(child), 1);
            }
        }
    }

    createSVGElement(node, childs) {
        this.trimElementChilden(childs);
        let componentAtts = {};
        const i = ind++;
        switch (node.nodeName) {
            case 'svg':
                componentAtts = this.obtainComponentAtts(node, SVG_ATTS);
                if (this.props.width) {
                    componentAtts.width = this.props.width;
                }
                if (this.props.height) {
                    componentAtts.height = this.props.height;
                }

                return (
                    <Svg key={i} {...componentAtts}>
                        {childs}
                    </Svg>
                );
            case 'g':
                componentAtts = this.obtainComponentAtts(node, G_ATTS);
                return (
                    <G key={i} {...componentAtts}>
                        {childs}
                    </G>
                );
            case 'path':
                componentAtts = this.obtainComponentAtts(node, PATH_ATTS);
                return (
                    <Path key={i} {...componentAtts}>
                        {childs}
                    </Path>
                );
            case 'circle':
                componentAtts = this.obtainComponentAtts(node, CIRCLE_ATTS);
                return (
                    <Circle key={i} {...componentAtts}>
                        {childs}
                    </Circle>
                );
            case 'rect':
                componentAtts = this.obtainComponentAtts(node, RECT_ATTS);
                return (
                    <Rect key={i} {...componentAtts}>
                        {childs}
                    </Rect>
                );
            case 'line':
                componentAtts = this.obtainComponentAtts(node, LINE_ATTS);
                return (
                    <Line key={i} {...componentAtts}>
                        {childs}
                    </Line>
                );
            case 'defs':
                return <Defs key={i}>{childs}</Defs>;
            case 'linearGradient':
                componentAtts = this.obtainComponentAtts(node, LINEARG_ATTS);
                return (
                    <LinearGradient key={i} {...componentAtts}>
                        {childs}
                    </LinearGradient>
                );
            case 'radialGradient':
                componentAtts = this.obtainComponentAtts(node, RADIALG_ATTS);
                return (
                    <RadialGradient key={i} {...componentAtts}>
                        {childs}
                    </RadialGradient>
                );
            case 'stop':
                componentAtts = this.obtainComponentAtts(node, STOP_ATTS);
                return (
                    <Stop key={i} {...componentAtts}>
                        {childs}
                    </Stop>
                );
            case 'ellipse':
                componentAtts = this.obtainComponentAtts(node, ELLIPSE_ATTS);
                return (
                    <Ellipse key={i} {...componentAtts}>
                        {childs}
                    </Ellipse>
                );
            case 'polygon':
                componentAtts = this.obtainComponentAtts(node, POLYGON_ATTS);
                return (
                    <Polygon key={i} {...componentAtts}>
                        {childs}
                    </Polygon>
                );
            case 'polyline':
                componentAtts = this.obtainComponentAtts(node, POLYLINE_ATTS);
                return (
                    <Polyline key={i} {...componentAtts}>
                        {childs}
                    </Polyline>
                );
            case 'text':
                componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
                return (
                    <Text key={i} {...componentAtts}>
                        {childs}
                    </Text>
                );
            case 'tspan':
                componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
                if (componentAtts.y) {
                    componentAtts.y = fixYPosition(componentAtts.y, node);
                }
                return (
                    <TSpan key={i} {...componentAtts}>
                        {childs}
                    </TSpan>
                );
            default:
                return null;
        }
    }

    obtainComponentAtts({ attributes }, enabledAttributes) {
        const styleAtts = {};

        if (this.state.fill && this.props.fillAll) {
            styleAtts.fill = this.state.fill;
        }

        Array.from(attributes).forEach(({ nodeName, nodeValue }) => {
            Object.assign(
                styleAtts,
                utils.transformStyle({
                    nodeName,
                    nodeValue,
                    fillProp: this.state.fill,
                }),
            );
        });

        const componentAtts = Array.from(attributes)
            .map(utils.camelCaseNodeName)
            .map(utils.removePixelsFromNodeValue)
            .filter(utils.getEnabledAttributes(enabledAttributes.concat(COMMON_ATTS)))
            .reduce((acc, { nodeName, nodeValue }) => {
                acc[nodeName] =
                    this.state.fill && nodeName === 'fill' && nodeValue !== 'none'
                        ? this.state.fill
                        : nodeValue;
                return acc;
            }, {});
        Object.assign(componentAtts, styleAtts);

        return componentAtts;
    }

    inspectNode(node) {
        // Only process accepted elements
        if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) {
            return <View key="node.nodeName" />;
        }

        // Process the xml node
        const arrayElements = [];

        // if have children process them.
        // Recursive function.
        if (node.childNodes && node.childNodes.length > 0) {
            for (let i = 0; i < node.childNodes.length; i++) {
                const isTextValue = node.childNodes[i].nodeValue;
                if (isTextValue) {
                    arrayElements.push(node.childNodes[i].nodeValue);
                } else {
                    const nodo = this.inspectNode(node.childNodes[i]);
                    if (nodo != null) {
                        arrayElements.push(nodo);
                    }
                }
            }
        }

        return this.createSVGElement(node, arrayElements);
    }

    render() {
        try {
            if (this.state.svgXmlData == null) {
                return null;
            }

            const inputSVG = this.state.svgXmlData
                .substring(
                    this.state.svgXmlData.indexOf('<svg '),
                    this.state.svgXmlData.indexOf('</svg>') + 6,
                )
                .replace(/<!-(.*?)->/g, '');

            const doc = new xmldom.DOMParser().parseFromString(inputSVG);

            const rootSVG = this.inspectNode(doc.childNodes[0]);

            return <View style={this.props.style}>{rootSVG}</View>;
        } catch (e) {
            console.error('ERROR SVG', e);
            return null;
        }
    }
}

SvgUri.propTypes = {
    style: PropTypes.object,
    width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    svgXmlData: PropTypes.string,
    source: PropTypes.any,
    fill: PropTypes.string,
    onLoad: PropTypes.func,
    fillAll: PropTypes.bool,
};

module.exports = SvgUri;


export const camelCase = value => value.replace(/-([a-z])/g, g => g[1].toUpperCase());

export const camelCaseNodeName = ({nodeName, nodeValue}) => ({nodeName: camelCase(nodeName), nodeValue});

export const removePixelsFromNodeValue = ({nodeName, nodeValue}) => ({nodeName, nodeValue: nodeValue.replace('px', '')});

export const transformStyle = ({nodeName, nodeValue, fillProp}) => {
  if (nodeName === 'style') {
    return nodeValue.split(';')
      .reduce((acc, attribute) => {
        const [property, value] = attribute.split(':');
        if (property == "")
            return acc;
        else
            return {...acc, [camelCase(property)]: fillProp && property === 'fill' ? fillProp : value};
      }, {});
  }
  return null;
};

export const getEnabledAttributes = enabledAttributes => ({nodeName}) => enabledAttributes.includes(camelCase(nodeName));

与大佬的唯一区别是style处理成了对象
大佬文章:https://blog.csdn.net/weixin_42341232/article/details/108900967

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值