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 | 行为 | 旋转动画(逐帧) | boolean | false |
pulse | 行为 | 脉冲动画(旋转 + 缩放) | boolean | false |
bounce | 行为 | 弹跳动画(垂直跳动) | boolean | false |
shake | 行为 | 抖动动画(水平抖动) | boolean | false |
beat | 行为 | 心跳动画(缩放跳动) | boolean | false |
fade | 行为 | 淡入淡出动画(透明度闪烁) | boolean | false |
icon | 内容 | 图标名称(pixelarticons 名称或映射别名) | string | — |
基础用法
使用 icon 属性指定图标名称。图标名称遵循 pixelarticons 的命名规范,同时支持常用别名——例如 spinner 会解析为 loader,x 会解析为 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 网格的倍数,以确保像素渲染清晰:
| 尺寸 | 像素 | 说明 |
|---|---|---|
xs | 12px | 超小 |
sm | 18px | 小 |
md | 24px | 默认 |
lg | 36px | 大 |
xl | 48px | 超大 |
2xl | 72px | 2 倍超大 |
1x / 2x / 3x | 24 / 48 / 72px | FontAwesome 兼容别名 |
<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 属性设置语义颜色(primary、success、warning、danger、info),或使用 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 属性镜像翻转图标(horizontal、vertical 或 both)。使用 rotation 属性旋转 90、180 或 270 度。两者可以组合使用。
<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() 实现逐帧效果,契合像素风格的美学:
| 动画 | 效果 | 帧数 |
|---|---|---|
spin | 360° 旋转 | 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 攻击。