Skip to content
0

图标使用

Teek 默认注册了全局组件 TkIcon,因此你可以通过该组件快捷引入图标。

TkIcon 默认支持如下类型的图标:

  • svg
  • unicode
  • iconfont
  • symbol
  • img
  • component
  • iconifyOffline
  • iconifyOnline

除此之外,您可以通过默认插槽传入自定义图标组件。

TkIcon 组件的基础使用以及 API 介绍请看 Icon 图标

提示

Teek 所有的 Icon 图标相关配置项,都使用 TkIcon 组件,因此怎么使用 TkIconicon 属性,就怎么使用图标相关配置项。

SVG 图标

您可以下载一个 svg 图标到项目里,然后传入 TkIcon 组件中。

在 Markdown 文档有两种格式输入:

vue
<script setup>
const icon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 320 192 704h639.936z"></path></svg>`;
</script>

<TkIcon :icon="icon" />
html
<TkIcon>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
    <path fill="currentColor" d="M512 320 192 704h639.936z"></path>
  </svg>
</TkIcon>

输出:

SVG 图标在哪里获取?您可以访问 阿里巴巴矢量图标库,然后搜索需要的图标,最后在下载时选择 复制 SVG 图标

除此之外,您也可以可以在该网站上下载 UnicodeFont ClassSymbol 图标,然后传入 TkIcon 组件中。

如:

html
<TkIcon :icon="icon-info" />

<!-- 如果是 Font Class,则等于如下 -->
<i class="iconfont icon-info"></i>

Iconify 图标

在线图标

如果您的项目部署在互联网上,那么可以使用 Iconify 的在线图标,只需要往 TkIcon 组件传入在线图标名称即可。

在 Markdown 文档输入:

html
<TkIcon icon="mdi:github" />

输出:

其中 mdi:github 为在线图标名,更多的在线图标请访问:Iconify 在线图标

如果项目部署在内网,或担心网络访问速度慢导致无法加载图标怎么办?往下看。

离线 JSON 图标

您可以直接将 Iconify 图标以 JSON 方式注册到本地,然后引入到 TkIcon 组件里,如:

sh
pnpm add @iconify-json/ant-design -d

然后在 .vitepress/theme/index.ts 里注册到 Teek 里:

ts
import { addIcons } from "vitepress-theme-teek";
import icons from "@iconify-json/ant-design/icons.json";

addIcons(icons);

最后和在线方式一样使用 TkIcon 组件:

html
<TkIcon icon="ant-design:account-book-filled" />

TkIcon 优先从已注册的图标名里获取,当获取不到时就会从互联网上下载。

这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。

Iconify JSON 图标的依赖名约定是 @iconify-json/{name},引入 JSON 图标数据的路径约定是 @iconify-json/{name}/icons.json

离线 Icon 图标

您可以直接将 Iconify 的图标集合安装到本地,然后引入到 TkIcon 组件里,如:

sh
pnpm add @iconify-icons/ant-design -d

然后使用:

vue
<script setup>
import Upload from "@iconify-icons/ant-design/upload";
</script>

<TkIcon :icon="Upload" />

这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。

Iconify Icon 图标的依赖名约定是 @iconify-icons/{name} 或者 @iconify/icons-{name}

两个离线图标方式对比

  • JSON 图标方式需要在项目初始化时注册进去,后续直接通过字符串引用
  • Icon 图标方式在每次使用时需要手动引入

信息

TkIcon 并没有实现 Iconify 相关逻辑,而是通过代理 Iconify 的 API 实现。

内置图标

SVG 图标

下面展示 Teek 内置的主题图标集合,Teek 只保留了自身引用的图标,您可以在页面上随处可见,如果您需要额外的图标,请参考上面的方式添加。

html
<ul class="demo-icons">
  <li v-for="(icon, name) in TkIcons" class="flx-column-center" @click="handleCopySvg(name)">
    <TkIcon :icon="icon" :size="30" />
    <span>{{ name }}</span>
  </li>
</ul>

当点击图标后将会复制引用图标代码(参考下面高亮部分)到您的剪切板中,然后就可以粘贴到代码中:

vue
<script setup>
import { yourClickIconName } from "vitepress-theme-teek/icons";
</script>

<TkIcon :icon="yourClickIconName" />

社交图标(iconfont)v1.4.1

如下是 Teek 内置的社交图标集合,可以在导航栏、侧边栏、社交配置 social 里快速应用。

html
<ul class="demo-icons">
  <li v-for="(icon) in iconfont" class="flx-column-center" @click="handleCopyIconfont(icon)">
    <i :class="`iconfont ${icon}`" style="font-size: 24px"></i>
    <span>{{ icon }}</span>
  </li>
</ul>

如果 Teek 提供的社交图标不满足您的要求,您可以使用访问 阿里巴巴矢量图标库 来下载您需要的任何图标。

vue
<script lang="ts" setup>
import { TkMessage, useClipboard, isClient } from "vitepress-theme-teek";
import * as TkIcons from "vitepress-theme-teek/icons";

const { copy, copied } = useClipboard();

const icon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 320 192 704h639.936z"></path></svg>`;

const handleCopySvg = async (name: string) => {
  await copy(`import { ${name} } from "vitepress-theme-teek/icons";`);
  copied.value
    ? TkMessage.success({ message: "复制成功!", plain: true })
    : TkMessage.error({ message: "复制失败!", plain: true });
};

const { extractIconClasses } = useIconfont();
const iconfont = extractIconClasses().map(item => item.className);

const handleCopyIconfont = async (icon: string) => {
  await copy(`<i class="iconfont ${icon}"></i>`);
  copied.value
    ? TkMessage.success({ message: "复制成功!", plain: true })
    : TkMessage.error({ message: "复制失败!", plain: true });
};

/**
 * iconfont 图标提取工具
 */
function useIconfont() {
  interface IconfontType {
    className: string;
    unicode?: string;
  }

  /**
   * 提取单个 CSS 规则中的 iconfont 信息
   */
  function extractIconFromRule(rule: CSSRule): IconfontType | null {
    if (!(rule instanceof CSSStyleRule)) return null;

    const { selectorText, style } = rule;
    // 只处理以 .icon- 开头且包含 ::before 的选择器
    if (
      !selectorText?.startsWith(".icon-") ||
      !selectorText.includes("::before")
    )
      return null;

    const className = selectorText.substring(
      1,
      selectorText.indexOf("::before")
    );
    const content = style.getPropertyValue("content");
    if (!content) return null;

    const unicode = content.replace(/['"\\]/g, "");
    return {
      className,
      unicode: unicode ? `&#x${getUnicode(unicode)};` : undefined,
    };
  }

  /**
   * 提取所有样式表中的 iconfont class 信息
   */
  function extractIconClasses(): IconfontType[] {
    if(!isClient) return [];

    const iconInfos: IconfontType[] = [];
    const processedSheets = new WeakSet<CSSStyleSheet>();

    for (const sheet of Array.from(document.styleSheets)) {
      // 防止跨域或重复处理
      if (processedSheets.has(sheet)) continue;
      processedSheets.add(sheet);

      let rules: CSSRuleList | undefined;
      try {
        rules = sheet.cssRules;
      } catch {}
      if (!rules) continue;

      for (const rule of Array.from(rules)) {
        const iconInfo = extractIconFromRule(rule);
        if (iconInfo) {
          iconInfos.push(iconInfo);
        }
      }
    }

    return iconInfos;
  }

  /**
   * 获取字符的 unicode 16 进制字符串
   * @param charCode 字符
   */
  const getUnicode = (charCode: string): string => {
    if (!charCode) return "";
    return charCode.codePointAt(0)?.toString(16).padStart(4, "0") ?? "";
  };

  return { extractIconClasses };
}
</script>

<style>
.demo-icons {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  overflow: hidden;
  list-style: none !important;
  border-left: 1px solid var(--vp-c-divider);
  border-top: 1px solid var(--vp-c-divider);
  border-radius: 4px;
  padding: 0 !important;
}

.demo-icons li {
  text-align: center;
  height: 90px;
  border-right: 1px solid var(--vp-c-divider);
  border-bottom: 1px solid var(--vp-c-divider);
  transition: background-color 0.3s;
  cursor: pointer;
  margin: 0 !important;
}

.demo-icons span {
  margin-top: 8px;
  font-size: 13px;
}

.demo-icons li:hover {
  background-color: var(--tk-fill-color-light);
}
</style>
最近更新