自定义svg图标组件,使用filter:drop-shadow你踩坑了吗?

460 阅读2分钟

前情回顾

公司想搞一套特色的图标库,不想使用antd组件自带的图标库。 于是封装了一个使用UI提供的svg图标的组件。

活不多说,直接上代码:

<template>
  <div
    class="svg-icon-box"
    :style="{width: size + 'px', height: size + 'px', 'line-height': size + 'px', cursor: cursor ? 'pointer' : 'auto', overflow: 'hidden'}"
    @mouseover="changeImageColor(true)"
    @mouseleave="changeImageColor(false)"
    @click="clickFun($event)"
  >
    <svg
      :class="svgClass"
      aria-hidden="true"
      :style="{
        fontSize: size + 'px',
        filter: self ? 'none' : 'drop-shadow(' + iconColor + ' 0px -1000px)',
        transform: self ? 'none' : 'translate(0px, 1000px)'
      }">
      <use :xlink:href="iconName"/>
    </svg>
  </div>
</template>

<script>
export default {
  name: 'YIcon',
  props: {
    type: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    },
    color: {
      type: String,
      default: '#5D6E7F'
    },
    size: {
      type: [Number, String],
      default: 16
    },
    cursor: {
      type: [Boolean, String, Number],
      default: false
    },
    hover: {
      type: String,
      default: ''
    },
    self: {
      type: [Boolean, String, Number],
      default: false
    },
  },
  data() {
    return {
      isHover: false
    }
  },
  computed: {
    iconColor() {
      return this.isHover ? this.hover || this.color : this.color
    },
    iconName() {
      return this.type.startsWith('icon-') ? `#${this.type}` : `#icon-${this.type}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    }
  },
  methods: {
    changeImageColor(flag) {
      if (this.self) return
      this.isHover = flag
    },
    clickFun(e) {
      this.$emit('click', e)
    }
  }
}
</script>
<style scoped>
.svg-icon-box {
  display: inline-block;
  vertical-align: middle;
}
.svg-icon {
  width: 1em;
  height: 1em;
  /* vertical-align: -0.15em; */
  fill: currentColor;
  overflow: hidden;
}
</style>

由于UI制作的svg,其中的fill属性都是固定值,我们用组件引用的时候,无法直接修改图标的颜色。

所以,看上图代码,我采用了迂回的方式,使用filter:drop-shadow投影出图标样式,然后设置颜色,再将原图通过transform:translate(0px, 1000px)和外层overflow: 'hidden'移出可视范围。在Chrome上测试很OK!

于是兴奋地提交了代码,部署到测试环境,用了个把月,很OK!

天有不测风云,有个领导电脑上,看页面时图标出现莫名其妙的BUG,显示不出现,然后鼠标悬浮又出现,移开鼠标,或者滚动鼠标都会出现闪现的BUG,关键是只有他电脑有问题,其他人电脑上都是没问题的,一阵定位,毫无头绪!

开始,看这现象就是有东西遮挡,然后各种控制台调试,就是找不到遮挡的元素!

Two thousand years later 。。。

在本地先是安装了低版本Firefox,问题未复现,然后安装了高版本Firefox,问题必现了!

对开发来说,找到必现条件,问题就解决了一半了。

然后,本地电脑上也没有找到遮挡的元素。。。

方向肯定是错了,一开始我也没想到是filter:drop-shadow的浏览器支持问题,毕竟还是展示了,但我尝试过去除这个属性之后,就没有这个问题了。

于是,我敲定就是这个样式在高版本Firefox上的支持有问题。

解决方案

不能使用filter:drop-shadow样式 需要改变svg颜色 那只能修改svg代码,将fill=“具体色值”的属性,改成fill="currentColor"

然后去找UI小姐姐,想让她制作svg图标的时候,直接就用这个值,然后失望了。。。

UI小姐姐制作svg时毕竟不是直接写代码,这个值只能设置某个色值或者none

所以,只有拿到图标后全局替换了。

好在问题解决了。

代码如下

<template>
  <div
    class="svg-icon-box"
    :style="{
      width: size + 'px',
      height: size + 'px',
      'line-height': size + 'px',
      cursor: cursor ? typeof cursor === 'string' ? cursor : 'pointer' : 'auto',
      overflow: 'hidden'
    }"
    @mouseover="changeImageColor(true)"
    @mouseleave="changeImageColor(false)"
    @click="clickFun($event)"
  >
    <svg
      :class="svgClass"
      aria-hidden="true"
      :style="{
        fontSize: size + 'px',
        fill: 'currentColor',
        color: iconColor
      }">
      <use :xlink:href="iconName"/>
    </svg>
  </div>
</template>

<script>
export default {
  name: 'YIcon',
  props: {
    type: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    },
    color: {
      type: String,
      default: '#5D6E7F'
    },
    size: {
      type: [Number, String],
      default: 16
    },
    cursor: {
      type: [Boolean, String],
      default: false
    },
    hover: {
      type: String,
      default: ''
    },
    self: {
      type: [Boolean, String, Number],
      default: false
    },
  },
  data() {
    return {
      isHover: false
    }
  },
  computed: {
    iconColor() {
      return this.isHover ? this.hover || this.color : this.color
    },
    iconName() {
      return this.type.startsWith('icon-') ? `#${this.type}` : `#icon-${this.type}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    }
  },
  methods: {
    changeImageColor(flag) {
      if (this.self) return
      this.isHover = flag
    },
    clickFun(e) {
      this.$emit('click', e)
    }
  }
}
</script>
<style scoped>
.svg-icon-box {
  display: inline-block;
  vertical-align: middle;
}
.svg-icon {
  width: 1em;
  height: 1em;
  /* vertical-align: -0.15em; */
  fill: currentColor;
  overflow: hidden;
}
</style>

当然,使用UI小姐姐提供的svg图标库,还需要在main.js中做一些配置,还有webpack配置,由于跟本篇想要表达的问题无关就不赘述了,有兴趣的朋友,可以上网搜搜,或者留言,我加更!

如果读此文章有收获,希望帮忙点个赞,也可以留言互相学习,谢谢各位老铁!