如何在Taro小程序环境中实现SVG矢量图

52 阅读1分钟

SVG的优点

SVG(Scalable Vector Graphics)是一种基于XML的矢量图形格式,它可以直接嵌入HTML中,使用<svg>标签来表示。SVG有很多强大之处,下面简要介绍:

  1. 矢量图形,无限缩放:SVG是矢量图形,放大缩小不失真,适合响应式设计和各种分辨率屏幕。
  2. 直接嵌入HTML,易于操作:SVG可以直接写在HTML中,成为DOM的一部分,可以使用CSS和JavaScript直接控制和交互。
  3. 文件体积小:对于简单的图形,SVG文件通常比位图(如PNG、JPEG)更小,加载更快。
  4. 可访问性和SEO友好:SVG中的文本是真实的文本,可以被屏幕阅读器读取,也可以被搜索引擎索引。
  5. 动画和交互:SVG支持CSS动画和JavaScript操作,可以创建复杂的动画和交互效果。

虽然svg如此好用,在大部分场景可以兼顾开发效率和良好的视觉体验。但是,Taro环境无法直接使用svg标签,因此需要考虑svg在Taro小程序中的实现方式。

总体思路

直接转换SVG为Base64编码的SVG图片;再创建一个Image对象,将SVG作为其src;思路虽简单,但在实际应用时仍发现一些不容忽视的问题:

问题1:rgba格式透明度丢失问题

  // 颜色格式验证函数,解决rbga透明度丢失问题
  const normalizeColor = (colorStr) => {
    if (colorStr.startsWith('rgba')) return colorStr;
    if (colorStr.startsWith('rgb'))
      return colorStr.replace('rgb', 'rgba').replace(')', ', 1)');
    // 处理十六进制颜色
    if (colorStr.startsWith('#')) {
      const hex = colorStr.replace('#', '');
      const r = parseInt(hex.slice(0, 2), 16);
      const g = parseInt(hex.slice(2, 4), 16);
      const b = parseInt(hex.slice(4, 6), 16);
      return `rgba(${r}, ${g}, ${b}, 1)`;
    }
    return colorStr;
  };

问题2:真机没有bota方法的情况

btoa 是将字符串进行 base64 编码的方法。它接收一个字符串参数,返回编码后的 base64 字符串。在浏览器环境中,btoa 是全局可用的方法,但在 Node.js 环境中不可用。

// 兼容真机的base64编码函数
const safeBtoa = (str: string): string => {
  try {
    // 如果btoa可用,直接使用
    if (typeof btoa !== 'undefined') {
      return btoa(str);
    }
    // 兼容方案:手动实现base64编码
    const chars =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    let output = '';

    for (let i = 0; i < str.length; i += 3) {
      const a = str.charCodeAt(i);
      const b = str.charCodeAt(i + 1);
      const c = str.charCodeAt(i + 2);
      const bitmap = (a << 16) | (b << 8) | c;

      output += chars.charAt((bitmap >> 18) & 63);
      output += chars.charAt((bitmap >> 12) & 63);
      output += isNaN(b) ? '=' : chars.charAt((bitmap >> 6) & 63);
      output += isNaN(c) ? '=' : chars.charAt(bitmap & 63);
    }

    return output;
  } catch (error) {
    console.error('Base64 encoding failed:', error);
    return '';
  }
};

最终组件:

// svg实现箭头组件,箭头有圆角,箭头向下;宽 15px,高 6px
const Arrow: React.FC<ArrowProps> = ({
  className = '',
  color = 'rgba(0,0,0,0.5)',
  onClick,
}) => {

  const normalizedColor = normalizeColor(color);
  const svgBase64 = safeBtoa(`
    <svg width="30" height="12" viewBox="0 0 15 6" fill="${normalizedColor}" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0L15 0V1C14.5915 1 14.3873 1 14.1902 1.02624C13.7623 1.08321 13.3517 1.23186 12.9865 1.46204C12.8183 1.56805 12.6614 1.69879 12.3477 1.96028L10.5729 3.43926C9.47972 4.35024 8.93313 4.80572 8.323 4.97978C7.78506 5.13324 7.21494 5.13324 6.677 4.97978C6.06687 4.80572 5.52028 4.35024 4.42711 3.43926L2.65233 1.96028C2.33855 1.69879 2.18166 1.56805 2.01346 1.46204C1.64825 1.23186 1.23768 1.08321 0.809761 1.02624C0.612675 1 0.40845 1 0 1V0Z"/>
    </svg>
  `);

  return (
    <View className={`arrow-down ${className}`} onClick={onClick}>
      <Image
        className='arrow-image'
        src={`data:image/svg+xml;base64,${svgBase64}`}
        mode='widthFix'
      />
    </View>
  );
};

export default Arrow;