阿里iconfont挂了之后...小程序项目内维护图标的方案

最近项目迭代要加些页面,在蓝湖上下好图标素材准备上传到阿里iconfont上去。打开网站一看,好家伙,维护中...

使用方法和源码可以直接跳到 我的方法-使用。

前言

但我还是菜鸟的时候,公司的老前端和我说,我挂了阿里都不会挂,叫我放心用阿里的iconfont。现在阿里挂了,图标没法换,项目没发上线,要是被老板发现了,我也要一起挂了。
怎么办?突然想到了后台管理项目的图标管理方案,使用svg雪碧图。可是看了一下小程序的文档,小程序不支持svg标签,只能使用image载入src,这样的话就不能改颜色了...
这可不行,于是百度了一下,找到了2种方法。

百度来的方法

drop-shadow

使用css的drop-shadow属性可以让我们给一个元素添加长得一摸一样的阴影,给阴影设置个颜色,把源元素隐藏起来只显示阴影不就达到换色的效果了吗。
emmm,试了一下,在开发者工具上果然可以,拿起iphone调试一看,一片空白....
为什么呢?搜索了一下社区,原来在ios上,如果把源元素隐藏了,阴影也不会被渲染。还好底下评论有解决方案,给加一个transform:translateZ(0);强制gpu渲染。于是又试了一下,效果也不太理想,ios上有的图标出来了,有的出不来,android上是彻底出不来了。
那只能换个思路,源元素隐藏阴影出不来,这样的话网上有人通过设置border-right使源元素始终显示,这样行不行呢?试了一下,android正常了,ios还是上一个方法的样子,有的出的来,有的出不来。排查了一下这些图标,出的来的都是线条贴边占满整个svg的图标,现在我又不能控制ui提供svg是啥样,设置阴影换颜色的方法只能放弃了。

mask

css里可以给元素设置一个mask然后改变背景颜色可以曲线实现svg换色

.colorful {
    display: inline-block;
    width: 32px; height: 32px;
    background-color: #f4615c;
    -webkit-mask: url(./xin.svg) no-repeat;
    mask: url(./xin.svg) no-repeat;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
}

实际试了一下,还是老样子,android可以,ios不行。

我的方法

这下就难住了,真的要提桶跑路了吗。绝望的时候看了一下之前的小程序使用阿里iconfont方案mini-program-iconfont-cli,发现作者的这个方案其实就是拉取存在阿里iconfont的svg,读取修改svg的字符串,通过组件传入颜色用js改变svg字符串的颜色属性,最后内联到标签的background-image



有了思路就好说了,剩下的都是体力活,不过在开始编码之前先要明确一下需求。

需求

  1. 兼容项目之前的iconfont组件写法,最好能做到无痛平替。
  2. 兼容原生小程序和uniapp。
  3. 图标能传入高度(size),且能根据高度算出宽度,使其能保持宽高比。
  4. 传入颜色(color)时能改变图标颜色,不传入不改变颜色使其保持源图标源颜色。

编写过程

首先为了兼容原生小程序和uniapp,就必须得使用原生的小程序组件来写。在uniapp项目的src/wxcomponents下新建一个iconfont原生组件。

iShot_2022-06-23_16.49.12.png

上面的需求有几个prop参数,size默认给个18、color颜色、还有一个就是让组件做到用的是哪个图标的name。在data里需要一个变量svgStyle存处理好的backgroundImage字符串和宽高。

Component({
  properties: {
    name: {
      type: String
    },
    color: {
      type: String
    },
    size: {
      type: Number,
      value: 18
    }
  },
  data: {
    svgStyle: ""
  },
})

wxml里就很简单,放个容器标签就行


wxss里设置组件host为行内元素就行

:host{
  display: inline-block;
}

当这个组件加载时首先要通过小程序的api和图标name读取放在项目内的svg字符串,然后做一些处理。这里我把svg图标都放在uniapp项目下的src/static/icon中。

{
  lifetimes: {
    attached() {
      const fs = wx.getFileSystemManager();
      try {
        const res = fs.readFileSync(
          `/static/icon/${this.data.name}.svg`,
          "utf8",
          0
        );
        // 读取到之后需要删掉换行符避免报错,且只提取svg标签部分内容
        const svgStr = res.replaceAll("\n", "").match("")[0];
      } catch (e) {
        console.error(e);
      }
    }
  }
}

然后通过svg自带的宽高和传入的size得倒页面中实际的宽度,并替换掉源svg中的宽高属性。宽高一般在svg标签上,用正则提取。

// ...
        const divideIndex = svgStr.indexOf(">");

        let firstStr = svgStr.slice(0, divideIndex);
        let otherStr = svgStr.slice(divideIndex);

        // 根据size 修改height width
        const [heightString, heightValue] = firstStr.match(
          /height="(\d+(\.\d*|))(?:px|)"/
        );
        const [widthString, widthValue] = firstStr.match(
          /width="(\d+(\.\d*|))(?:px|)"/
        );

        const finalHeight = this.data.size;
        const finalWidth = (widthValue / heightValue) * finalHeight;

        firstStr = firstStr
          .replace(widthString, `width="${finalWidth}px"`)
          .replace(heightString, `height="${finalHeight}px"`);

接下来需要处理svg的颜色。svg的颜色属性fill一般放在svg标签上,如果有多个路径,可能也会在别的标签上,为了保险起见,我们都替换掉。而且还要移除一些影响展示效果的属性,如opacity什么的。根据需求不传color的时候不改颜色,这里要做一下判断处理。

// ...
      if (this.data.color) {
          firstStr += ` fill="${this.data.color}"`;
          otherStr = otherStr.replaceAll(
            /fill="(.*?)"/g,
            `fill="${this.data.color}"`
          );
          otherStr = otherStr.replaceAll(/opacity="(.*?)"/g, "");
        }

最后需要把标签转译一下放到backgound-image里,并加上宽高苏醒设置到svgStyle上。最后就大功告成了。

// ...
       let clStr = (firstStr + otherStr)
          .replaceAll("<", "%3C")
          .replaceAll(">", "%3E")
          .replaceAll('"', `'`)
          .replaceAll("#", "%23");

        const resultStr = `background-image:url("data:image/svg+xml, ${clStr}");width:${finalWidth}px;height:${finalHeight}px`;
        this.setData({
          svgStyle: resultStr
        });

使用

源码地址 mp-svg-icon

首先将图标放在static目录的img文件夹下

  • unapp 的static在src下
  • 原生小程序的static在项目根目录下


    iShot_2022-06-23_17.29.54.png

下载好组件源码后,uniapp一定要放在src/wxcomponents里,原生无所谓。

想全局使用原生组件的话需要在page.json里注册组件
uniapp在globalStyleusingComponents注册

{
  "globalStyle": {
    "usingComponents": {
      "iconfont": "/wxcomponents/iconfont/iconfont",
    }
  },
}

原生小程序直接在usingComponents注册

{
  "usingComponents": {
    "iconfont": "components/iconfont/iconfont"
  },
}

最后在页面中使用就行


  

需要注意的是size为px单位,如果是蓝湖750的设计稿需要除以2。

最后

一点想法

  • 小程序为什么这么麻烦?
  • 阿里iconfont不可靠,就算好了也不敢用了。

文章来源:阿里iconfont挂了之后...小程序项目内维护图标的方案 - Devin's Blog (yw3.fun)

你可能感兴趣的:(阿里iconfont挂了之后...小程序项目内维护图标的方案)