VIcon
🌟 Introduction
Le composant VIcon
est un composant Vue.js permettant d'afficher des icônes avec une large gamme d'options de personnalisation, y compris des animations, des couleurs, et des tailles. Il est conçu pour être flexible et performant, avec une prise en charge des différentes options d'affichage, de flip, et de titres accessibles.
Il a exactement la même API que OhVueIcon, et utilise @iconify/vue
sous le capot.
Attention
Les noms des icônes doivent être ceux de Iconify-vue.
📐 Structure
Le composant VIcon
s'intègre facilement en utilisant la syntaxe suivante :
<VIcon name="nom-collection:nom-de-l-icone" :scale="1.5" color="#FF5733" animation="spin" />
<VIcon name="ri:alert-fill" :scale="1.5" color="#FF5733" animation="ring" />
Migration depuis 5.x
Pour les noms de collection qui ne contiennent pas de tiret (-
), il est accepté de séparer le nom de la collection du nom de l’icône avec un tiret -
.
<VIcon name="ri-alert-fill" :scale="1.5" color="#FF5733" animation="ring" />
Ceci rend le composant VIcon totalement compatible avec OhVueIcon si n’étaient utilisées que les icônes RemixIcon et quelques autres collections.
DX
Pour l’expérience développeur, il est conseillé d’utiliser l’extension vscode antfu.iconify.
🛠️ Props
Voici les différentes propriétés que vous pouvez utiliser avec ce composant :
Prop | Type | Défaut | Description |
---|---|---|---|
name | string | Obligatoire | Le nom de l'icône à afficher. |
scale | string | number | 1 | Échelle de l'icône, avec un facteur multiplicateur de la taille par défaut. |
verticalAlign | string | '-0.2em' | Alignement vertical de l'icône par rapport à la ligne de base. |
animation | 'spin' | 'wrench' | 'pulse' | 'spin-pulse' | 'flash' | 'float' | undefined | Type d'animation appliqué à l'icône. |
speed | 'fast' | 'slow' | undefined | Vitesse de l'animation si elle est définie. |
flip | 'horizontal' | 'vertical' | 'both' | undefined | Inverse l'icône horizontalement, verticalement ou les deux. |
label | string | undefined | Étiquette ARIA pour l'accessibilité. |
title | string | undefined | Titre de l'icône (balise <title> ), utilisé pour l'accessibilité et les info-bulles. |
color | string | undefined | Couleur principale de l'icône. |
fill | string | undefined | Couleur de remplissage de l'icône (utilise in fine color comme conseillé dans la doc de @iconify/vue). Cette prop n’existe que pour la rétrocompatibilité avec OhVueIcon, préférer l’utilisation de la prop color . |
inverse | boolean | false | Applique une couleur inversée à l'icône. |
ssr | boolean | false | Active le rendu côté serveur (Server-Side Rendering). |
display | 'block' | 'inline-block' | 'inline' | 'inline-block' | Définit le mode d'affichage de l'icône. |
📡 Événements
Ce composant ne déclenche pas d'événements personnalisés.
🧩 Slots
Ce composant ne contient pas de slots.
⚙️ Code source du composant
<script lang="ts" setup>
import VIcon from '../VIcon.vue'
</script>
<template>
<div class="flex justify-between p-4">
<section>
<header class="fr-text--lg">
Simple :
</header>
<p>
<VIcon
name="ri-close-line"
/>
<VIcon
name="ri-checkbox-circle-line"
/>
</p>
</section>
<section>
<header class="fr-text--lg">
Plus grande :
</header>
<p>
<VIcon
name="ri-close-line"
scale="2"
/>
<VIcon
name="ri-checkbox-circle-line"
scale="2"
/>
</p>
</section>
<section>
<header class="fr-text--lg">
Animée :
</header>
<p>
spin :
<VIcon
name="ri-loader-4-line"
animation="spin"
/>
</p>
<p>
spin-pulse :
<VIcon
name="ri-loader-4-line"
animation="spin-pulse"
/>
</p>
<p>
pulse :
<VIcon
name="ri-checkbox-circle-line"
animation="pulse"
/>
</p>
<p>
flash :
<VIcon
name="ri-close-line"
animation="flash"
/>
</p>
<p>
float :
<VIcon
name="ri-close-line"
animation="float"
/>
</p>
<p>
ring :
<VIcon
name="fa6-regular:bell"
animation="ring"
/>
</p>
<p>
wrench :
<VIcon
name="twemoji:rolling-on-the-floor-laughing"
animation="wrench"
/>
</p>
</section>
<section>
<header class="fr-text--lg">
Colorée :
</header>
<p>
lightgreen :
<VIcon
name="ri-checkbox-circle-line"
color="lightgreen"
/>
</p>
<p>
#f90 :
<VIcon
name="ri-checkbox-circle-line"
color="#f90"
/>
</p>
<p>
var(--blue-france-main-525) :
<VIcon
name="ri-checkbox-circle-line"
color="var(--blue-france-main-525)"
/>
</p>
</section>
</div>
</template>
<script lang="ts" setup>
import { Icon } from '@iconify/vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import type { VIconProps } from './VIcon.types'
export type { VIconProps }
const props = withDefaults(defineProps<VIconProps>(), {
scale: 1,
verticalAlign: '-0.2em',
display: 'inline-block',
})
const icon = ref<{ $el: SVGElement } | null>(null)
const fontSize = computed(() => `${+props.scale * 1.2}rem`)
const flip = computed(() => {
if (props.flip === 'both') {
return 'horizontal,vertical'
}
return props.flip
})
watch(() => props.title, setTitle)
async function setTitle () {
if (!(icon.value?.$el)) {
return
}
const titleExists = !!(icon.value?.$el).querySelector('title')
const titleEl = document.createElement('title')
if (!props.title) {
titleEl.remove()
return
}
titleEl.innerHTML = props.title
await nextTick()
if (!titleExists) {
(icon.value?.$el as SVGElement).firstChild?.before(titleEl)
}
}
onMounted(setTitle)
const finalName = computed(() => {
return props.name?.startsWith('vi-') ? props.name.replace(/vi-(.*)/, 'vscode-icons:$1') : props.name ?? ''
})
const finalColor = computed(() => {
return props.color ?? props.fill ?? 'inherit'
})
</script>
<template>
<Icon
ref="icon"
:icon="finalName"
:style="{ fontSize, verticalAlign, display }"
:aria-label="label"
class="vicon"
:class="{
'vicon-spin': props.animation === 'spin',
'vicon-wrench': props.animation === 'wrench',
'vicon-pulse': props.animation === 'pulse',
'vicon-spin-pulse': props.animation === 'spin-pulse',
'vicon-flash': props.animation === 'flash',
'vicon-float': props.animation === 'float',
'vicon-ring': props.animation === 'ring',
'vicon-slow': props.speed === 'slow',
'vicon-fast': props.speed === 'fast',
'vicon-inverse': props.inverse,
}"
:flip
:ssr
/>
</template>
<style scoped>
.vicon {
color: v-bind(finalColor);
}
.vicon-inverse {
color: #fff;
}
/* ---------------- spin ---------------- */
.vicon-spin:not(.vicon-hover),
.vicon-spin.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin {
animation: vicon-spin 1s linear infinite;
}
.vicon-spin:not(.vicon-hover).vicon-fast,
.vicon-spin.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin.vicon-fast {
animation: vicon-spin 0.7s linear infinite;
}
.vicon-spin:not(.vicon-hover).vicon-slow,
.vicon-spin.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin.vicon-slow {
animation: vicon-spin 2s linear infinite;
}
/* ---------------- spin-pulse ---------------- */
.vicon-spin-pulse:not(.vicon-hover),
.vicon-spin-pulse.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin-pulse {
animation: vicon-spin 1s infinite steps(8);
}
.vicon-spin-pulse:not(.vicon-hover).vicon-fast,
.vicon-spin-pulse.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin-pulse.vicon-fast {
animation: vicon-spin 0.7s infinite steps(8);
}
.vicon-spin-pulse:not(.vicon-hover).vicon-slow,
.vicon-spin-pulse.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-spin-pulse.vicon-slow {
animation: vicon-spin 2s infinite steps(8);
}
@keyframes vicon-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* ---------------- wrench ---------------- */
.vicon-wrench:not(.vicon-hover),
.vicon-wrench.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-wrench {
animation: vicon-wrench 2.5s ease infinite;
}
.vicon-wrench:not(.vicon-hover).vicon-fast,
.vicon-wrench.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-wrench.vicon-fast {
animation: vicon-wrench 1.2s ease infinite;
}
.vicon-wrench:not(.vicon-hover).vicon-slow,
.vicon-wrench.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-wrench.vicon-slow {
animation: vicon-wrench 3.7s ease infinite;
}
@keyframes vicon-wrench {
0% {
transform: rotate(-12deg);
}
8% {
transform: rotate(12deg);
}
10%, 28%, 30%, 48%, 50%, 68% {
transform: rotate(24deg);
}
18%, 20%, 38%, 40%, 58%, 60% {
transform: rotate(-24deg);
}
75%, 100% {
transform: rotate(0deg);
}
}
/* ---------------- ring ---------------- */
.vicon-ring:not(.vicon-hover),
.vicon-ring.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-ring {
animation: vicon-ring 2s ease infinite;
}
.vicon-ring:not(.vicon-hover).vicon-fast,
.vicon-ring.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-ring.vicon-fast {
animation: vicon-ring 1s ease infinite;
}
.vicon-ring:not(.vicon-hover).vicon-slow,
.vicon-ring.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-ring.vicon-slow {
animation: vicon-ring 3s ease infinite;
}
@keyframes vicon-ring {
0% {
transform: rotate(-15deg);
}
2% {
transform: rotate(15deg);
}
4%, 12% {
transform: rotate(-18deg);
}
6% {
transform: rotate(18deg);
}
8% {
transform: rotate(-22deg);
}
10% {
transform: rotate(22deg);
}
12% {
transform: rotate(-18deg);
}
14% {
transform: rotate(18deg);
}
16% {
transform: rotate(-12deg);
}
18% {
transform: rotate(12deg);
}
20%, 100% {
transform: rotate(0deg);
}
}
/* ---------------- pulse ---------------- */
.vicon-pulse:not(.vicon-hover),
.vicon-pulse.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-pulse {
animation: vicon-pulse 2s linear infinite;
}
.vicon-pulse:not(.vicon-hover).vicon-fast,
.vicon-pulse.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-pulse.vicon-fast {
animation: vicon-pulse 1s linear infinite;
}
.vicon-pulse:not(.vicon-hover).vicon-slow,
.vicon-pulse.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-pulse.vicon-slow {
animation: vicon-pulse 3s linear infinite;
}
@keyframes vicon-pulse {
0% {
transform: scale(1.1);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1.1);
}
}
/* ---------------- flash ---------------- */
.vicon-flash:not(.vicon-hover),
.vicon-flash.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-flash {
animation: vicon-flash 2s ease infinite;
}
.vicon-flash:not(.vicon-hover).vicon-fast,
.vicon-flash.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-flash.vicon-fast {
animation: vicon-flash 1s ease infinite;
}
.vicon-flash:not(.vicon-hover).vicon-slow,
.vicon-flash.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-flash.vicon-slow {
animation: vicon-flash 3s ease infinite;
}
@keyframes vicon-flash {
0%, 100%, 50%{
opacity: 1;
}
25%, 75%{
opacity: 0;
}
}
/* ---------------- float ---------------- */
.vicon-float:not(.vicon-hover),
.vicon-float.vicon-hover:hover,
.vicon-parent.vicon-hover:hover > .vicon-float {
animation: vicon-float 2s linear infinite;
}
.vicon-float:not(.vicon-hover).vicon-fast,
.vicon-float.vicon-hover.vicon-fast:hover,
.vicon-parent.vicon-hover:hover > .vicon-float.vicon-fast {
animation: vicon-float 1s linear infinite;
}
.vicon-float:not(.vicon-hover).vicon-slow,
.vicon-float.vicon-hover.vicon-slow:hover,
.vicon-parent.vicon-hover:hover > .vicon-float.vicon-slow {
animation: vicon-float 3s linear infinite;
}
@keyframes vicon-float {
0%, 100% {
transform: translateY(-3px);
}
50% {
transform: translateY(3px);
}
}
</style>