簡介
為徹底實現 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<myIconurl="//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
<myIconurl="//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 時,添加fillsvgElement.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><Iconv-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><iv-else="props.type":class="className":style="{fontSize: props.size + 'px',color: props.color,...newStyle,}"style="display: inline-block"></i>
</template>