Skip to content

Icon 图标

Sakana Element 内置了 pixelarticons 的全部 486 个图标——一个基于 24×24 网格的像素风格图标库。所有图标开箱即用,无需额外安装。

名称分类说明类型默认值
flip? 风格翻转方向'horizontal' | 'vertical' | 'both'
rotation? 风格旋转角度90 | 180 | 270
size? 尺寸图标大小'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '1x' | '2x' | '3x''md'
type? 颜色图标类型颜色'primary' | 'success' | 'warning' | 'danger' | 'info'
color? 颜色自定义颜色string
spin? 行为旋转动画(逐帧)booleanfalse
pulse? 行为脉冲动画(旋转 + 缩放)booleanfalse
bounce? 行为弹跳动画(垂直跳动)booleanfalse
shake? 行为抖动动画(水平抖动)booleanfalse
beat? 行为心跳动画(缩放跳动)booleanfalse
fade? 行为淡入淡出动画(透明度闪烁)booleanfalse
icon? 内容图标名称(pixelarticons 名称或映射别名)string

基础用法

使用 icon 属性指定图标名称。图标名称遵循 pixelarticons 的命名规范,同时支持常用别名——例如 spinner 会解析为 loaderx 会解析为 close

<template>
  <div class="demo-icon">
    <px-icon icon="home" />
    <px-icon icon="user" />
    <px-icon icon="search" />
    <px-icon icon="bookmark" />
    <px-icon icon="heart" />
    <px-icon icon="sliders" />
  </div>
</template>

<style scoped>
.demo-icon {
  display: flex;
  gap: 20px;
  font-size: 24px;
}
</style>

不同尺寸

使用 size 属性设置图标大小。所有尺寸均为 24px 网格的倍数,以确保像素渲染清晰:

尺寸像素说明
xs12px超小
sm18px
md24px默认
lg36px
xl48px超大
2xl72px2 倍超大
1x / 2x / 3x24 / 48 / 72pxFontAwesome 兼容别名
<template>
  <div class="demo-icon">
    <px-icon icon="bookmark" size="xs" />
    <px-icon icon="bookmark" size="sm" />
    <px-icon icon="bookmark" size="lg" />
    <px-icon icon="bookmark" size="xl" />
    <px-icon icon="bookmark" size="2xl" />
    <px-icon icon="bookmark" size="3x" />
  </div>
</template>

<style scoped>
.demo-icon {
  display: flex;
  align-items: center;
  gap: 20px;
}
</style>

图标颜色

使用 type 属性设置语义颜色(primarysuccesswarningdangerinfo),或使用 color 属性设置任意自定义颜色。同时设置时,color 会覆盖 type

<template>
  <div class="demo-icon">
    <px-icon icon="check" type="primary" size="2x" />
    <px-icon icon="check" type="success" size="2x" />
    <px-icon icon="warning-box" type="warning" size="2x" />
    <px-icon icon="close-box" type="danger" size="2x" />
    <px-icon icon="info-box" type="info" size="2x" />
    <px-icon icon="heart" color="#e91e63" size="2x" />
  </div>
</template>

<style scoped>
.demo-icon {
  display: flex;
  align-items: center;
  gap: 20px;
}
</style>

翻转与旋转

使用 flip 属性镜像翻转图标(horizontalverticalboth)。使用 rotation 属性旋转 90180270 度。两者可以组合使用。

<template>
  <div class="demo-icon-flip">
    <h4>Flip / 翻转</h4>
    <div class="demo-row">
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" />
        <span>original</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" flip="horizontal" />
        <span>horizontal</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" flip="vertical" />
        <span>vertical</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" flip="both" />
        <span>both</span>
      </div>
    </div>

    <h4>Rotation / 旋转</h4>
    <div class="demo-row">
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" />
        <span>0°</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" :rotation="90" />
        <span>90°</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" :rotation="180" />
        <span>180°</span>
      </div>
      <div class="icon-item">
        <px-icon icon="arrow-right" size="2x" :rotation="270" />
        <span>270°</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
.demo-icon-flip h4 {
  margin: 0 0 12px;
  font-size: 14px;
  color: #888;
}
.demo-icon-flip h4:not(:first-child) {
  margin-top: 20px;
}
.demo-row {
  display: flex;
  align-items: center;
  gap: 30px;
}
.icon-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.icon-item span {
  font-size: 12px;
  color: #666;
}
</style>

图标动画

所有动画使用 CSS steps() 实现逐帧效果,契合像素风格的美学:

动画效果帧数
spin360° 旋转8 步
pulse旋转 + 缩放8 步
bounce垂直弹跳5 步
shake水平抖动4 步
beat缩放跳动3 步
fade透明度闪烁4 步
<template>
  <div class="demo-icon">
    <div class="icon-item">
      <px-icon icon="loader" spin size="2x" />
      <span>spin</span>
    </div>
    <div class="icon-item">
      <px-icon icon="loader" pulse size="2x" />
      <span>pulse</span>
    </div>
    <div class="icon-item">
      <px-icon icon="notification" shake size="2x" />
      <span>shake</span>
    </div>
    <div class="icon-item">
      <px-icon icon="heart" beat size="2x" type="danger" />
      <span>beat</span>
    </div>
    <div class="icon-item">
      <px-icon icon="zap" bounce size="2x" type="warning" />
      <span>bounce</span>
    </div>
    <div class="icon-item">
      <px-icon icon="circle" fade size="2x" />
      <span>fade</span>
    </div>
  </div>
</template>

<style scoped>
.demo-icon {
  display: flex;
  align-items: center;
  gap: 30px;
}
.icon-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.icon-item span {
  font-size: 12px;
  color: #666;
}
</style>

图标集合

浏览和搜索所有 486 个内置图标。点击任意图标即可复制其名称。

<script setup lang="ts">
import { defaultPixelIcons } from 'sakana-element';
import { computed, ref } from 'vue';

const search = ref('');
const copiedName = ref('');

const allIconNames = computed(() => Object.keys(defaultPixelIcons).sort());

const filteredIcons = computed(() => {
  const keyword = search.value.trim().toLowerCase();
  if (!keyword) return allIconNames.value;
  return allIconNames.value.filter((name) => name.toLowerCase().includes(keyword));
});

function copyIconName(name: string) {
  navigator.clipboard.writeText(name).then(() => {
    copiedName.value = name;
    setTimeout(() => {
      copiedName.value = '';
    }, 1500);
  });
}
</script>

<template>
  <div class="icon-collection">
    <div class="icon-search">
      <px-icon icon="search" size="sm" class="search-icon" />
      <input
        v-model="search"
        type="text"
        class="search-input"
        placeholder="Search icon name..."
      />
      <span class="icon-count">{{ filteredIcons.length }} icons</span>
    </div>

    <div v-if="filteredIcons.length" class="icon-grid">
      <div
        v-for="name in filteredIcons"
        :key="name"
        class="icon-card"
        :class="{ 'icon-card--copied': copiedName === name }"
        :title="`Click to copy: ${name}`"
        @click="copyIconName(name)"
      >
        <px-icon :icon="name" size="lg" />
        <span class="icon-name">{{ name }}</span>
        <span v-if="copiedName === name" class="copied-tip">Copied!</span>
      </div>
    </div>

    <div v-else class="icon-empty">
      <px-icon icon="search" size="2x" />
      <p>No icons found for "{{ search }}"</p>
    </div>
  </div>
</template>

<style scoped>
.icon-collection {
  width: 100%;
}

.icon-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 2px solid var(--px-color-border, #ccc);
  background: var(--px-color-bg, #fff);
  margin-bottom: 16px;
}

.search-icon {
  color: var(--px-color-text-placeholder, #999);
  flex-shrink: 0;
}

.search-input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--px-font-family, monospace);
  font-size: 14px;
  color: var(--px-color-text, #333);
}

.search-input::placeholder {
  color: var(--px-color-text-placeholder, #999);
}

.icon-count {
  font-size: 12px;
  color: var(--px-color-text-secondary, #888);
  white-space: nowrap;
  flex-shrink: 0;
}

.icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  gap: 8px;
  max-height: 520px;
  overflow-y: auto;
  padding: 4px;
}

.icon-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px 4px 8px;
  border: 2px solid var(--px-color-border-light, #e0e0e0);
  background: var(--px-color-bg, #fff);
  cursor: pointer;
  position: relative;
  transition: border-color 0.15s, background-color 0.15s;
}

.icon-card:hover {
  border-color: var(--px-color-primary, #7287fd);
  background: var(--px-color-primary-light-9, #eef1fe);
}

.icon-card--copied {
  border-color: var(--px-color-success, #40a070);
  background: var(--px-color-success-light-9, #e2f7eb);
}

.icon-name {
  font-size: 10px;
  color: var(--px-color-text-secondary, #888);
  text-align: center;
  word-break: break-all;
  line-height: 1.3;
  max-width: 100%;
}

.copied-tip {
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 10px;
  color: var(--px-color-success, #40a070);
  font-weight: bold;
}

.icon-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 40px;
  color: var(--px-color-text-placeholder, #999);
}

.icon-empty p {
  margin: 0;
  font-size: 14px;
}
</style>

自定义图标

注册自定义 SVG 图标,与内置图标一起使用。

单个注册

ts
import { registerPixelIcon } from 'sakana-element';

registerPixelIcon('my-icon', '<svg viewBox="0 0 24 24">...</svg>');
vue
<px-icon icon="my-icon" />

批量注册

ts
import { registerPixelIcons } from 'sakana-element';

registerPixelIcons({
  'my-icon-a': '<svg viewBox="0 0 24 24">...</svg>',
  'my-icon-b': '<svg viewBox="0 0 24 24">...</svg>',
});

注册 API

函数说明
registerPixelIcon(name, svg)注册单个自定义图标
registerPixelIcons(icons)批量注册多个图标
hasPixelIcon(name)检查图标名称是否已注册
getRegisteredIconNames()获取所有已注册图标名称的数组
getPixelIcon(name)获取已注册图标的 SVG 字符串
resolveIconName(name)将图标名称别名解析为 pixelarticons 名称
getIconNameMap()获取完整的图标名称别名映射

TIP

SVG 内容在注册时会自动进行安全过滤——<script> 等危险元素和事件处理属性会被移除,以防止 XSS 攻击。