svg动态改变颜色以及在vite的应用

3,560 阅读2分钟

原因

1 拿到的 svg 图片如果是自带颜色的,一般父元素的颜色设置就会无效。

2 想让 svg 图片的颜色跟随父元素的 color 值变化,可先用编辑器打开 svg 图片文件,修改fill属性。

注意,因为 path 标签中的 d=”..."很长,而 fill="..." 通常放在标签的末尾,容易看漏。

举个例子: 父元素设置 red 无效,因为<path>标签的最后默认设置了 blue,此时该 svg 图片是 蓝色的

<div style="color: red">
  <svg width="1em" height="1em" viewBox="0 0 1024 1024">
    <path d="M863.1 518.5H505.5V160.9c0-4.4-3.6-8-8..." fill="blue" />
  </svg>
</div>

解决方案

1 在 svg 标签中加上 fill="currentColor"

2 在 path 标签中去掉 fill="...",或者改为 fill="currentColor" 注意fill="white"不能去掉否则svg显示不出来

改为以下代码即可,svg 图片 红色生效:

<div style="color: red">
  <svg width="1em" height="1em" fill="currentColor" viewBox="0 0 1024 1024">
    <path d="M863.1 518.5H505.5V160.9c0-4.4-3.6-8-8..." />
  </svg>
</div>

脚本批量修改

const fs = require('fs')
const Spinnies = require('spinnies')

const spinnies = new Spinnies()

const inputFileDir = 'src/icons'
// 生成的文件不加入版本管理
const outputFileDir = 'src/assets/icons'
readdir(inputFileDir)

// 读取指定目录下的所有文件

function readdir(path) {
  fs.readdir(path, async function (err, files) {
    if (err) {
      console.log('Error', err)
      return
    }
    deleteFolder(outputFileDir)
    fs.mkdirSync(outputFileDir, { recursive: true })
    spinnies.add('file', { text: 'svg build...' })
    const results = files.map(async (file) => {
      const content: any = await readfile(`${path}/${file}`)
      const result = await writefile(`${outputFileDir}/${file}`, content.data, file)
      return result
    })
    // 所有都写入完成输出打印信息
    Promise.all(results)
      .then((res) => {
        const str = `export default [${res}]`
        fs.writeFile(`${outputFileDir}/index.ts`, str, function (error) {
          if (error) {
            console.log('Error', error)
          }
        })
        spinnies.succeed('file', { text: `✨ svg create success in ${outputFileDir}` })
      })
      .catch((errs) => {
        spinnies.fail('file', { text: errs })
      })
  })
}
// 写入文件
function writefile(path, data, file) {
  return new Promise((resolve, reject) => {
    fs.writeFile(path, data, function (errs) {
      if (errs) reject(errs)
      resolve(`"${file}"`)
    })
  })
}
// 读取单个文件
function readfile(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
    //正则替换全局fill="***" 除了fill="white"
      data = data.replace(/fill="(?!white).*"/g, '')
      if (err) reject(err)
      resolve({ path, data })
    })
  })
}
// 递归删除文件
function deleteFolder(filePath) {
  if (fs.existsSync(filePath)) {
    const files = fs.readdirSync(filePath)
    files.forEach((file) => {
      const nextFilePath = `${filePath}/${file}`
      const states = fs.statSync(nextFilePath)
      states.isDirectory() ? deleteFolder(nextFilePath) : fs.unlinkSync(nextFilePath)
    })
    fs.rmdirSync(filePath)
  }
}

在vite中使用svg

vite-plugin-svg-icons

1 安装

npm i vite-plugin-svg-icons -D

2 配置

// vite.config.ts
import viteSvgIcons from 'vite-plugin-svg-icons';
import path from 'path';

export default () => {
  return {
    plugins: [
      viteSvgIcons({
        // 指定需要缓存的图标文件夹
        iconDirs: [path.resolve(process.cwd(), 'src/icons')],
        // 指定symbolId格式
        symbolId: 'icon-[dir]-[name]',
      }),
    ],
  };
};

3 在 src/main.ts 内引入注册脚本

import 'virtual:svg-icons-register';

4 封装组件

// SvgIcon.vue
<template>
  <svg
    aria-hidden="true"
    :class="[$attrs.class, 'svg-icon']"
    :style="getStyle"
    @mouseenter="color = hover"
    @mouseleave="color = normal"
  >
    <use :href="symbolId" :fill="color" />
  </svg>
</template>

<script setup lang="ts">
import type { CSSProperties } from 'vue'

const props = defineProps({
  prefix: {
    type: String,
    default: 'icon'
  },
  name: {
    type: String,
    required: true
  },
  size: {
    type: [Number, String],
    default: 16
  },
  normal: {
    type: String,
    default: ''
  },
  hover: {
    type: String,
    default: ''
  }
})
const { normal, hover } = toRefs(props)
const color = ref('')
onMounted(() => {
  color.value = normal.value
})
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
const getStyle = computed((): CSSProperties => {
  const { size } = props
  let s = `${size}`
  s = `${s.replace('px', '')}px`
  return {
    width: s,
    height: s
  }
})
</script>

<style scoped>
.svg-icon {
  display: inline-block;
  overflow: hidden;
  vertical-align: -0.15em;
  fill: currentColor;
}
</style>

5 使用组件,只需要传入名字即可

// demo.vue
<SvgIcon :name="icon" size="16" normal="#409EFF" hover="#67C23A"></SvgIcon>

其他详细用法看官方文档 vite-plugin-svg-icons

崔学社

最后给大家安利一个神秘学习组织,崔学社,里面有大佬 阿崔 mini-vue 作者,github4k多的赞 ,b站地址,每天都在群里安利一些很硬的干货,真的很硬(手动害羞),希望更多小伙伴加入我们的团伙~