简介
为彻底实现 vue3 项目图标自由,特开发此 npm包 vue3-icon-sui
,全品类图标,通通支持!
- iconify 图标
- svg 图标
- font-class 图标
安装
npm i vue3-icon-sui -S
使用
按需导入
任意页面中
import myIcon from "vue3-icon-sui";
myIcon 可为任意自定义的组件名称
全局注册
src/main.ts
import myIcon from "vue3-icon-sui";
const app = createApp(App);
app.component("myIcon", myIcon);
iconify 图标
- 必要传参 icon – iconify 官网图标的名称,支持翻转 flip
- 水平翻转 “horizontal”
- 垂直翻转 “vertical”
- 水平垂直翻转 “horizontal vertical”
- 在 iconify 官网搜索想要的图标
https://icon-sets.iconify.design/?query=home
2. 页面中使用
传给属性 icon
<myIcon icon="ic:baseline-home" color="red" size="36" />
svg 图标(支持多彩)
- 必要传参 name – 项目的
src/assets/icons/svg
目录中svg 图标的名称
从https://www.iconfont.cn/ 中找到喜欢的图标
鼠标悬浮其上时,点击下载
可直接下载svg,将其放入项目的
src/assets/icons/svg
目录中,改名为 nice.svg也可以复制 svg 代码,新建
src/assets/icons/svg/nice.svg
文件,再将 svg 代码粘贴到 nice.svg 中页面中使用
将 svg 图标的名称传给属性 name<myIcon name="nice" size="36" />
效果如下
传入 color 可自定义颜色,但会丧失多彩<myIcon name="nice" size="36" color="red" />
font-class 图标
- 从https://www.iconfont.cn/ 中找到喜欢的图标
鼠标悬浮其上时,点击添加入库
加入已有项目,或新建项目
得到 url
//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css
鼠标悬浮在项目的图标上,可一键复制代码,得到 type
icon-nice
页面中使用
必传参数 url 和 type<myIcon url="//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css" type="icon-nice" size="36" color="red" />
属性
属性名 | 属性值 | 说明 |
---|---|---|
icon | iconify 官网图标的名称,如 “ic:baseline-home” | iconify图标必传 |
flip | 水平翻转 “horizontal” 垂直翻转 “vertical” 水平垂直翻转 “horizontal vertical” |
仅iconify图标支持 |
name | 项目的 src/assets/icons/svg 目录中svg 图标的名称 | svg图标必传 |
rotate | 旋转度数,数值即可,如 90 即顺时针旋转90度 | 所有图标都支持 |
color | 颜色,如 red | 所有图标都支持,但svg的多彩图标会变为纯色 |
size | 大小,数值,如 36 即 36px | 所有图标都支持 |
url | font-class图标的css地址,详见使用范例 | font-class图标必传 |
type | font-class图标的代码,详见使用范例 | font-class图标必传 |
fontFamily | font-class图标的前缀 | 除非在项目设置中进行了修改,否则使用默认的 icon-font 即可 |
font-class 图标自定义fontFamily
通常不建议修改!
假设修改为 myfont ,则页面使用时,fontFamily属性需传入 myfont
<myIcon
url="//at.alicdn.com/t/c/font_2261937_vumtsyzbq7d.css"
type="icon-nice"
fontFamily="myfont"
/>
源码
详解见源码注释
<script setup lang="ts">
// 优先推荐【iconify图标】必要传参 icon ,支持翻转 flip
// 搜索图标 https://icon-sets.iconify.design/
// 【svg图标-支持多彩图标】必要传参 name
// 需将svg图标放在 src/assets/icons/svg 目录中,
// 【font图标-不支持多彩图标】必要传参 url 和 type
// 整个项目使用多个图标时,只需有一个图标传入 url 即可
import { computed, onBeforeMount, ref, watch, onMounted } from "vue";
import { Icon } from "@iconify/vue";
// 接收的属性
const props = defineProps({
icon: {
type: String,
},
// 水平翻转 "horizontal"
// 垂直翻转 "vertical"
// 水平垂直翻转 "horizontal vertical"
flip: {
type: String,
},
name: {
type: String,
},
// 旋转角度
rotate: {
type: Number,
},
// 图标颜色
color: {
type: String,
},
// 图标大小
size: {
type: [Number, String],
default: 16,
},
url: {
type: String,
default: "//at.alicdn.com/t/c/font_2261937_dg35xe8b86.css",
},
type: {
type: String,
},
fontFamily: {
type: String,
default: "iconfont",
},
});
onBeforeMount(() => {
if (props.url) {
const existingLink = document.querySelector(`link[href="${props.url}"]`);
if (!existingLink) {
const link = document.createElement("link");
link.href = props.url;
link.rel = "stylesheet";
document.head.appendChild(link);
}
}
});
const className = computed(() => `${props.fontFamily} ${props.type}`);
// 计算样式
const newStyle = computed(() => {
const style: Record<string, string | number> = {};
if (props.size) {
style.width = `${props.size}px`;
style.height = `${props.size}px`;
}
if (props.color) {
style.color = props.color;
}
if (props.rotate) {
style.transform = `rotate(${props.rotate}deg)`;
}
return style;
});
// 状态管理
const svgContainer = ref<HTMLDivElement>();
const loading = ref(true);
const error = ref(null);
// 加载并渲染SVG的函数
const loadAndRenderSvg = async () => {
try {
// 重置状态
loading.value = true;
error.value = null;
// 使用动态import导入SVG文件,获取原始内容
// 加上 ?raw 后,Vite 会直接将 SVG 文件的内容以纯文本字符串的形式返回
const svgUrl = "/src/assets/icons/svg/" + props.name + ".svg";
const module = await import(svgUrl + "?raw");
const svgContent = module.default;
// 清空容器
if (svgContainer.value) {
(svgContainer.value as any).innerHTML = "";
}
// 创建临时元素解析SVG内容
const tempDiv = document.createElement("div");
tempDiv.innerHTML = svgContent;
// 获取SVG元素
const svgElement = tempDiv.querySelector("svg");
if (!svgElement) {
throw new Error("导入的文件不是有效的SVG");
}
// 设置SVG属性
svgElement.setAttribute("width", props.size + "px");
svgElement.setAttribute("height", props.size + "px");
if (props.color) {
// 替换SVG颜色
replaceSvgFillColor(svgElement, props.color);
// svg 图片本身没有 fill 时,添加fill
svgElement.setAttribute("fill", props.color);
}
// 将SVG元素添加到容器
if (svgContainer.value) {
svgContainer.value.appendChild(svgElement);
}
} catch (err: any) {
error.value = err.message || `无法加载图标: ${props.name}`;
} finally {
loading.value = false;
}
};
/**
* 替换SVG元素的fill颜色值
* @param svgElement 目标SVG元素
* @param newColor 新的颜色值(可以是十六进制、rgb、rgba或颜色名称)
* @returns 是否成功替换颜色
*/
function replaceSvgFillColor(
svgElement: SVGElement,
newColor: string
): boolean {
if (!svgElement) {
return false;
}
try {
// 查找所有带有fill属性的路径元素
const pathElements = svgElement.querySelectorAll("path[fill]");
if (pathElements.length === 0) {
return false;
}
// 替换每个path元素的fill属性
pathElements.forEach((path) => {
path.setAttribute("fill", newColor);
});
return true;
} catch (error) {
return false;
}
}
// 监听props变化,重新加载图标
watch(
() => [props.name, props.size, props.color],
() => {
loadAndRenderSvg();
}
);
// 组件挂载时加载图标
onMounted(() => {
if (props.name) {
loadAndRenderSvg();
}
});
</script>
<template>
<Icon
v-if="props.icon"
:icon="props.icon"
:style="newStyle"
:flip="props.flip"
/>
<template v-else-if="props.name">
<!-- 加载状态 -->
<div v-if="loading" class="loading">加载中...</div>
<!-- 错误状态 -->
<div v-if="error" class="error">图标加载失败: {{ error }}</div>
<!-- SVG容器 - 动态渲染的SVG将插入到这里 -->
<div ref="svgContainer" v-else></div>
</template>
<i
v-else="props.type"
:class="className"
:style="{
fontSize: props.size + 'px',
color: props.color,
...newStyle,
}"
style="display: inline-block"
>
</i>
</template>