Component Boom

This commit is contained in:
David Haz
2025-07-10 15:36:38 +03:00
parent a4982577ad
commit 9b3465b04d
135 changed files with 16697 additions and 60 deletions

View File

@@ -0,0 +1,113 @@
<template>
<div class="blur-text-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative min-h-[400px] overflow-hidden">
<RefreshButton @refresh="forceRerender" />
<BlurText :key="rerenderKey" text="Isn't this so cool?!" :delay="delay"
class-name="text-3xl md:text-6xl font-bold text-center" :animate-by="animateBy" :direction="direction"
:threshold="threshold" :root-margin="rootMargin" :step-duration="stepDuration"
@animation-complete="() => { showCallback && showToast() }" />
</div>
<Customize>
<PreviewSwitch title="Show Completion Toast" v-model="showCallback" @update:model-value="forceRerender" />
<div class="flex gap-4 flex-wrap">
<button
class="text-xs bg-[#0b0b0b] rounded-[10px] border border-[#1e3721] hover:bg-[#1e3721] text-white h-8 px-3 transition-colors"
@click="toggleAnimateBy">
Animate By: <span class="text-[#a1a1aa]">&nbsp;{{ animateBy }}</span>
</button>
<button
class="text-xs bg-[#0b0b0b] rounded-[10px] border border-[#1e3721] hover:bg-[#1e3721] text-white h-8 px-3 transition-colors"
@click="toggleDirection">
Direction: <span class="text-[#a1a1aa]">&nbsp;{{ direction }}</span>
</button>
</div>
<PreviewSlider title="Delay (ms)" v-model="delay" :min="50" :max="500" :step="10"
@update:model-value="forceRerender" />
<PreviewSlider title="Step Duration (s)" v-model="stepDuration" :min="0.1" :max="1" :step="0.05"
@update:model-value="forceRerender" />
<PreviewSlider title="Threshold" v-model="threshold" :min="0.1" :max="1" :step="0.1"
@update:model-value="forceRerender" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['motion-v']" />
</template>
<template #code>
<CodeExample :code-object="blurText" />
</template>
<template #cli>
<CliInstallation :command="blurText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import RefreshButton from '../../components/common/RefreshButton.vue'
import PropTable from '../../components/common/PropTable.vue'
import Dependencies from '../../components/code/Dependencies.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import BlurText from '../../content/TextAnimations/BlurText/BlurText.vue'
import { blurText } from '@/constants/code/TextAnimations/blurTextCode'
import { useToast } from 'primevue/usetoast'
import { useForceRerender } from '@/composables/useForceRerender'
const animateBy = ref<'words' | 'letters'>('words')
const direction = ref<'top' | 'bottom'>('top')
const delay = ref(200)
const stepDuration = ref(0.35)
const threshold = ref(0.1)
const rootMargin = ref('0px')
const showCallback = ref(true)
const toast = useToast()
const { rerenderKey, forceRerender } = useForceRerender()
const toggleAnimateBy = () => {
animateBy.value = animateBy.value === 'words' ? 'letters' : 'words'
forceRerender()
}
const toggleDirection = () => {
direction.value = direction.value === 'top' ? 'bottom' : 'top'
forceRerender()
}
const showToast = () => {
toast.add({
severity: 'secondary',
summary: 'Animation Finished!',
life: 3000
})
}
const propData = [
{ name: 'text', type: 'string', default: '""', description: 'The text content to animate.' },
{ name: 'animateBy', type: 'string', default: '"words"', description: 'Determines whether to animate by "words" or "letters".' },
{ name: 'direction', type: 'string', default: '"top"', description: 'Direction from which the words/letters appear ("top" or "bottom").' },
{ name: 'delay', type: 'number', default: '200', description: 'Delay between animations for each word/letter (in ms).' },
{ name: 'stepDuration', type: 'number', default: '0.35', description: 'The time taken for each letter/word to animate (in seconds).' },
{ name: 'threshold', type: 'number', default: '0.1', description: 'Intersection threshold for triggering the animation.' },
{ name: 'rootMargin', type: 'string', default: '"0px"', description: 'Root margin for the intersection observer.' },
{ name: 'className', type: 'string', default: '""', description: 'Additional class names to style the component.' },
{ name: 'animationFrom', type: 'object', default: 'undefined', description: 'Custom initial animation properties.' },
{ name: 'animationTo', type: 'array', default: 'undefined', description: 'Custom target animation properties array.' },
{ name: 'easing', type: 'function', default: '(t) => t', description: 'Custom easing function for the animation.' },
{ name: 'onAnimationComplete', type: 'function', default: 'undefined', description: 'Callback function triggered when all animations complete.' }
]
</script>

View File

@@ -0,0 +1,75 @@
<template>
<div class="circular-text-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative min-h-[400px] overflow-hidden flex items-center justify-center">
<CircularText :key="rerenderKey" text="VUE * BITS * IS * AWESOME * " :spin-duration="spinDuration"
:on-hover="onHover" class-name="text-blue-500" />
</div>
<Customize>
<div class="flex gap-4 flex-wrap">
<button
class="text-xs bg-[#0b0b0b] rounded-[10px] border border-[#1e3721] hover:bg-[#1e3721] text-white h-8 px-3 transition-colors"
@click="toggleOnHover">
On Hover: <span class="text-[#a1a1aa]">&nbsp;{{ onHover }}</span>
</button>
</div>
<PreviewSlider title="Spin Duration (s)" v-model="spinDuration" :min="1" :max="50" :step="1"
@update:model-value="forceRerender" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['motion-v']" />
</template>
<template #code>
<CodeExample :code-object="circularText" />
</template>
<template #cli>
<CliInstallation :command="circularText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import Dependencies from '../../components/code/Dependencies.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import CircularText from '../../content/TextAnimations/CircularText/CircularText.vue'
import { circularText } from '@/constants/code/TextAnimations/circularTextCode'
import { useForceRerender } from '@/composables/useForceRerender'
const onHover = ref<'slowDown' | 'speedUp' | 'pause' | 'goBonkers'>('speedUp')
const spinDuration = ref(20)
const { rerenderKey, forceRerender } = useForceRerender()
const hoverOptions: Array<'slowDown' | 'speedUp' | 'pause' | 'goBonkers'> = [
'slowDown',
'speedUp',
'pause',
'goBonkers'
]
const toggleOnHover = () => {
const currentIndex = hoverOptions.indexOf(onHover.value)
const nextIndex = (currentIndex + 1) % hoverOptions.length
onHover.value = hoverOptions[nextIndex]
forceRerender()
}
const propData = [
{ name: 'text', type: 'string', default: '""', description: 'The text content to display in a circular pattern.' },
{ name: 'spinDuration', type: 'number', default: '20', description: 'Duration of one full rotation in seconds.' },
{ name: 'onHover', type: 'string', default: '"speedUp"', description: 'Hover behavior: "slowDown", "speedUp", "pause", or "goBonkers".' },
{ name: 'className', type: 'string', default: '""', description: 'Additional class names to style the component.' }
]
</script>

View File

@@ -0,0 +1,100 @@
<template>
<div class="curved-loop-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative h-[500px] overflow-hidden p-0">
<CurvedLoop :key="rerenderKey" :marquee-text="marqueeText" :speed="speed" :curve-amount="curveAmount"
:interactive="interactive" />
</div>
<Customize>
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Marquee Text</label>
<input v-model="marqueeText" type="text" placeholder="Enter text..."
class="w-[300px] px-3 py-2 bg-[#0b0b0b] border border-[#333] rounded-md text-white focus:outline-none focus:border-[#666]"
@input="forceRerender" />
</div>
<PreviewSlider title="Speed" v-model="speed" :min="0" :max="10" :step="0.1"
@update:model-value="forceRerender" />
<PreviewSlider title="Curve Amount" v-model="curveAmount" :min="-400" :max="400" :step="10" value-unit="px"
@update:model-value="forceRerender" />
<PreviewSwitch title="Draggable" :model-value="interactive"
@update:model-value="(val: boolean) => { interactive = val; forceRerender() }" />
</Customize>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="curvedLoop" />
</template>
<template #cli>
<CliInstallation :command="curvedLoop.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import CurvedLoop from '../../content/TextAnimations/CurvedLoop/CurvedLoop.vue'
import { curvedLoop } from '@/constants/code/TextAnimations/curvedLoopCode'
import { useForceRerender } from '@/composables/useForceRerender'
const marqueeText = ref('Be ✦ Creative ✦ With ✦ Vue ✦ Bits ✦')
const speed = ref(2)
const curveAmount = ref(400)
const interactive = ref(true)
const { rerenderKey, forceRerender } = useForceRerender()
const propData = [
{
name: 'marqueeText',
type: 'string',
default: '""',
description: 'The text to display in the curved marquee'
},
{
name: 'speed',
type: 'number',
default: '2',
description: 'Animation speed of the marquee text'
},
{
name: 'className',
type: 'string',
default: 'undefined',
description: 'CSS class name for styling the text'
},
{
name: 'curveAmount',
type: 'number',
default: '400',
description: 'Amount of curve in the text path'
},
{
name: 'direction',
type: '"left" | "right"',
default: '"left"',
description: 'Initial direction of the marquee animation'
},
{
name: 'interactive',
type: 'boolean',
default: 'true',
description: 'Whether the marquee can be dragged by the user'
}
]
</script>

View File

@@ -0,0 +1,160 @@
<template>
<div class="decrypted-text-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative py-6 overflow-hidden">
<RefreshButton @click="forceRerender" />
<div :key="key" class="pl-6 m-8 w-full flex flex-col justify-start items-start">
<DecryptedText :speed="speed" text="Ahoy, matey!" :max-iterations="maxIterations" :sequential="sequential"
:reveal-direction="revealDirection" parent-class-name="decrypted-text"
:use-original-chars-only="useOriginalCharsOnly" :animate-on="animateOn" />
<DecryptedText :speed="speed" text="Set yer eyes on this" :max-iterations="maxIterations"
:sequential="sequential" :reveal-direction="revealDirection" parent-class-name="decrypted-text"
:use-original-chars-only="useOriginalCharsOnly" :animate-on="animateOn" />
<DecryptedText :speed="speed" text="And try tinkerin' round'" :max-iterations="maxIterations"
:sequential="sequential" :reveal-direction="revealDirection" parent-class-name="decrypted-text"
:use-original-chars-only="useOriginalCharsOnly" :animate-on="animateOn" />
<DecryptedText :speed="speed" text="with these here props, arr!" :max-iterations="maxIterations"
:sequential="sequential" :reveal-direction="revealDirection" parent-class-name="decrypted-text"
:use-original-chars-only="useOriginalCharsOnly" :animate-on="animateOn"
@animation-complete="() => console.log('✅ Animation Finished!')" />
</div>
</div>
<Customize>
<PreviewSwitch title="Sequential" v-model="sequential" @update:model-value="forceRerender" />
<PreviewSwitch title="Original Chars" v-model="useOriginalCharsOnly" @update:model-value="forceRerender" />
<PreviewSlider title="Speed" v-model="speed" :min="10" :max="200" :step="10" value-unit="ms"
@update:model-value="forceRerender" />
<PreviewSlider title="Iterations" v-model="maxIterations" :min="1" :max="50" :step="1"
@update:model-value="forceRerender" />
<PreviewSelect title="Animation Trigger" v-model="animateOn" :options="animateOptions"
@update:model-value="(val) => { animateOn = val as 'view' | 'hover'; forceRerender(); }" />
<PreviewSelect title="Animation Direction" v-model="revealDirection" :options="directionOptions"
@update:model-value="(val) => { revealDirection = val as 'start' | 'end' | 'center'; forceRerender(); }" />
</Customize>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="decryptedText" />
</template>
<template #cli>
<CliInstallation :command="decryptedText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import PreviewSelect from '../../components/common/PreviewSelect.vue'
import RefreshButton from '../../components/common/RefreshButton.vue'
import DecryptedText from '../../content/TextAnimations/DecryptedText/DecryptedText.vue'
import { decryptedText } from '@/constants/code/TextAnimations/decryptedTextCode'
import { useForceRerender } from '@/composables/useForceRerender'
const { rerenderKey: key, forceRerender } = useForceRerender()
const speed = ref(60)
const maxIterations = ref(10)
const sequential = ref(true)
const useOriginalCharsOnly = ref(false)
const revealDirection = ref<'start' | 'end' | 'center'>('start')
const animateOn = ref<'view' | 'hover'>('view')
const animateOptions = [
{ label: 'View', value: 'view' },
{ label: 'Hover', value: 'hover' }
]
const directionOptions = [
{ label: 'Start', value: 'start' },
{ label: 'End', value: 'end' },
{ label: 'Center', value: 'center' }
]
const propData = [
{
name: 'text',
type: 'string',
default: '""',
description: 'The text content to decrypt.'
},
{
name: 'speed',
type: 'number',
default: '50',
description: 'Time in ms between each iteration.'
},
{
name: 'maxIterations',
type: 'number',
default: '10',
description: 'Max # of random iterations (non-sequential mode).'
},
{
name: 'sequential',
type: 'boolean',
default: 'false',
description: 'Whether to reveal one character at a time in sequence.'
},
{
name: 'revealDirection',
type: '"start" | "end" | "center"',
default: '"start"',
description: 'From which position characters begin to reveal in sequential mode.'
},
{
name: 'useOriginalCharsOnly',
type: 'boolean',
default: 'false',
description: 'Restrict scrambling to only the characters already in the text.'
},
{
name: 'className',
type: 'string',
default: '""',
description: 'CSS class for revealed characters.'
},
{
name: 'parentClassName',
type: 'string',
default: '""',
description: 'CSS class for the main characters container.'
},
{
name: 'encryptedClassName',
type: 'string',
default: '""',
description: 'CSS class for encrypted characters.'
},
{
name: 'animateOn',
type: '"view" | "hover"',
default: '"hover"',
description: 'Trigger scrambling on hover or scroll-into-view.'
}
]
</script>
<style scoped>
.decrypted-text {
font-size: 2rem;
line-height: 1.6;
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="falling-text-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative h-[400px] overflow-hidden p-0 flex justify-center items-center">
<FallingText :key="key"
text="Vue Bits is a library of animated and interactive Vue components designed to streamline UI development and simplify your workflow."
:highlight-words="['Vue', 'Bits', 'animated', 'components', 'simplify']" :trigger="trigger"
:gravity="gravity" font-size="2rem" :mouse-constraint-stiffness="mouseConstraintStiffness" />
<div class="absolute z-0 text-[4rem] font-[900] text-[#222] select-none" v-if="!effectStarted">
{{ trigger === 'hover' ? 'Hover Me' : trigger === 'click' ? 'Click Me' : 'Auto Start' }}
</div>
</div>
<Customize>
<PreviewSelect title="Animation Trigger" v-model="trigger" :options="triggerOptions"
@update:model-value="forceRerender" />
<PreviewSlider title="Gravity" v-model="gravity" :min="0.1" :max="2" :step="0.01"
@update:model-value="forceRerender" />
<PreviewSlider title="Mouse Constraint Stiffness" v-model="mouseConstraintStiffness" :min="0.1" :max="2"
:step="0.1" @update:model-value="forceRerender" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['matter-js']" />
</template>
<template #code>
<CodeExample :code-object="fallingText" />
</template>
<template #cli>
<CliInstallation :command="fallingText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import Dependencies from '../../components/code/Dependencies.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import PreviewSelect from '../../components/common/PreviewSelect.vue'
import FallingText from '../../content/TextAnimations/FallingText/FallingText.vue'
import { fallingText } from '@/constants/code/TextAnimations/fallingTextCode'
import { useForceRerender } from '@/composables/useForceRerender'
const { rerenderKey: key, forceRerender } = useForceRerender()
const gravity = ref(0.56)
const mouseConstraintStiffness = ref(0.9)
const trigger = ref<'hover' | 'click' | 'auto' | 'scroll'>('hover')
const effectStarted = computed(() => trigger.value === 'auto')
const triggerOptions = [
{ value: 'hover', label: 'Hover' },
{ value: 'click', label: 'Click' },
{ value: 'auto', label: 'Auto' },
{ value: 'scroll', label: 'Scroll' }
]
const propData = [
{
name: 'text',
type: 'string',
default: '""',
description: 'The text content to display and eventually animate.'
},
{
name: 'highlightWords',
type: 'string[]',
default: '[]',
description: 'List of words or substrings to apply a highlight style.'
},
{
name: 'trigger',
type: "'click' | 'hover' | 'auto' | 'scroll'",
default: '"auto"',
description: 'Defines how the falling effect is activated.'
},
{
name: 'backgroundColor',
type: 'string',
default: '"transparent"',
description: 'Canvas background color for the physics world.'
},
{
name: 'wireframes',
type: 'boolean',
default: 'false',
description: 'Whether to render the physics bodies in wireframe mode.'
},
{
name: 'gravity',
type: 'number',
default: '1',
description: 'Vertical gravity factor for the physics engine.'
},
{
name: 'mouseConstraintStiffness',
type: 'number',
default: '0.2',
description: 'Stiffness for the mouse drag constraint.'
},
{
name: 'fontSize',
type: 'string',
default: '"1rem"',
description: 'Font size applied to the text before it falls.'
}
]
</script>
<style scoped></style>

View File

@@ -0,0 +1,109 @@
<template>
<div class="fuzzy-text-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative h-[500px] overflow-hidden">
<div class="flex flex-col items-center justify-center h-full">
<FuzzyText :key="`main-${rerenderKey}`" text="404" :base-intensity="baseIntensity"
:hover-intensity="hoverIntensity" :enable-hover="enableHover" :font-size="140" />
<div class="my-1" />
<FuzzyText :key="`sub-${rerenderKey}`" text="not found" :base-intensity="baseIntensity"
:hover-intensity="hoverIntensity" :enable-hover="enableHover" :font-size="70" font-family="Gochi Hand" />
</div>
</div>
<Customize>
<PreviewSlider title="Base Intensity" v-model="baseIntensity" :min="0" :max="1" :step="0.01"
@update:model-value="forceRerender" />
<PreviewSlider title="Hover Intensity" v-model="hoverIntensity" :min="0" :max="2" :step="0.01"
@update:model-value="forceRerender" />
<PreviewSwitch title="Enable Hover" :model-value="enableHover"
@update:model-value="(val: boolean) => { enableHover = val; forceRerender() }" />
</Customize>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="fuzzyText" />
</template>
<template #cli>
<CliInstallation :command="fuzzyText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import FuzzyText from '../../content/TextAnimations/FuzzyText/FuzzyText.vue'
import { fuzzyText } from '@/constants/code/TextAnimations/fuzzyTextCode'
import { useForceRerender } from '@/composables/useForceRerender'
const baseIntensity = ref(0.2)
const hoverIntensity = ref(0.5)
const enableHover = ref(true)
const { rerenderKey, forceRerender } = useForceRerender()
const propData = [
{
name: 'text',
type: 'string',
default: '""',
description: 'The text content to display inside the fuzzy text component.'
},
{
name: 'fontSize',
type: 'number | string',
default: '"clamp(2rem, 8vw, 8rem)"',
description: 'Specifies the font size of the text. Accepts any valid CSS font-size value or a number (interpreted as pixels).'
},
{
name: 'fontWeight',
type: 'string | number',
default: '900',
description: 'Specifies the font weight of the text.'
},
{
name: 'fontFamily',
type: 'string',
default: '"inherit"',
description: 'Specifies the font family of the text. "inherit" uses the computed style from the parent.'
},
{
name: 'color',
type: 'string',
default: '#fff',
description: 'Specifies the text color.'
},
{
name: 'enableHover',
type: 'boolean',
default: 'true',
description: 'Enables the hover effect for the fuzzy text.'
},
{
name: 'baseIntensity',
type: 'number',
default: '0.18',
description: 'The fuzz intensity when the text is not hovered.'
},
{
name: 'hoverIntensity',
type: 'number',
default: '0.5',
description: 'The fuzz intensity when the text is hovered.'
}
]
</script>

View File

@@ -0,0 +1,121 @@
<template>
<div class="gradient-text-demo">
<TabbedLayout>
<template #preview>
<h2 class="demo-title-extra">Default</h2>
<div class="demo-container relative min-h-[150px] flex items-center justify-center">
<div class="text-[2rem]">
<GradientText text="Add a splash of color!" :colors="gradientPreview" :animation-speed="speed"
:show-border="false" />
</div>
</div>
<h2 class="demo-title-extra">Border Animation</h2>
<div class="demo-container relative min-h-[150px] flex items-center justify-center">
<div class="text-[2rem]">
<GradientText text="Now with a cool border!" :colors="gradientPreview" :animation-speed="speed"
:show-border="true" class-name="custom-gradient-class" />
</div>
</div>
<Customize>
<PreviewSlider title="Loop Duration" v-model="speed" :min="1" :max="10" :step="0.5" value-unit="s" />
<div class="flex flex-col gap-0">
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Colors</label>
<input v-model="colors" type="text" placeholder="Enter colors separated by commas" maxlength="100"
class="w-[300px] px-3 py-2 bg-[#0b0b0b] border border-[#333] rounded-md text-white focus:outline-none focus:border-[#666]" />
</div>
<div class="w-[300px] h-3 rounded-md border border-[#271E37]" :style="{
background: `linear-gradient(to right, ${gradientPreview.join(', ')})`
}" />
</div>
</Customize>
<p class="demo-extra-info mt-4 flex items-center gap-2">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clip-rule="evenodd" />
</svg>
For a smoother animation, the gradient should start and end with the same color.
</p>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="gradientText" />
</template>
<template #cli>
<CliInstallation :command="gradientText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import GradientText from '../../content/TextAnimations/GradientText/GradientText.vue'
import { gradientText } from '@/constants/code/TextAnimations/gradientTextCode'
const colors = ref('#40ffaa, #4079ff, #40ffaa, #4079ff, #40ffaa')
const speed = ref(3)
const gradientPreview = computed(() => colors.value.split(',').map(color => color.trim()))
const propData = [
{
name: 'text',
type: 'string',
default: '""',
description: 'The text content to be displayed with gradient effect.'
},
{
name: 'className',
type: 'string',
default: "''",
description: 'Adds custom classes to the root element for additional styling.'
},
{
name: 'colors',
type: 'string[]',
default: '["#ffaa40", "#9c40ff", "#ffaa40"]',
description: 'Defines the gradient colors for the text or border.'
},
{
name: 'animationSpeed',
type: 'number',
default: '8',
description: 'The duration of the gradient animation in seconds.'
},
{
name: 'showBorder',
type: 'boolean',
default: 'false',
description: 'Determines whether a border with the gradient effect is displayed.'
}
]
</script>
<style scoped>
.demo-title-extra {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 1rem;
color: #e5e7eb;
}
.demo-extra-info {
color: #a1a1aa;
font-size: 0.875rem;
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<div class="shiny-text-demo">
<TabbedLayout>
<template #preview>
<h2 class="demo-title-extra">Basic</h2>
<div class="demo-container relative min-h-[150px] text-2xl flex items-center justify-center">
<ShinyText text="Just some shiny text!" :disabled="false" :speed="3" class-name="shiny-text-demo" />
</div>
<h2 class="demo-title-extra">Button Text</h2>
<div class="demo-container relative min-h-[150px] text-2xl flex items-center justify-center">
<div class="shiny-button">
<ShinyText text="Shiny Button" :disabled="false" :speed="3" class-name="shiny-text-demo" />
</div>
</div>
<h2 class="demo-title-extra">Configurable Speed</h2>
<div class="demo-container relative min-h-[150px] text-2xl flex items-center justify-center">
<ShinyText :text="speed < 2.5 ? '🐎 This is fast!' : '🐌 This is slow!'" :disabled="false" :speed="speed"
class-name="shiny-text-demo" />
</div>
<Customize>
<PreviewSlider title="Animation Duration" v-model="speed" :min="1" :max="5" :step="0.1" value-unit="s" />
</Customize>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="shinyText" />
</template>
<template #cli>
<CliInstallation :command="shinyText.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import ShinyText from '../../content/TextAnimations/ShinyText/ShinyText.vue'
import { shinyText } from '@/constants/code/TextAnimations/shinyTextCode'
const speed = ref(3)
const propData = [
{
name: 'text',
type: 'string',
default: '-',
description: 'The text to be displayed with the shiny effect.'
},
{
name: 'disabled',
type: 'boolean',
default: 'false',
description: 'Disables the shiny effect when set to true.'
},
{
name: 'speed',
type: 'number',
default: '5',
description: 'Specifies the duration of the animation in seconds.'
},
{
name: 'className',
type: 'string',
default: "''",
description: 'Adds custom classes to the root element.'
}
]
</script>
<style scoped>
.shiny-button {
padding: 12px 24px;
border-radius: 50px;
cursor: pointer;
transition: transform 0.2s ease;
}
.shiny-button:hover {
transform: translateY(-2px);
}
.shiny-text-demo {
font-weight: 600;
}
</style>

View File

@@ -0,0 +1,111 @@
<template>
<div class="text-cursor-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative h-[500px] overflow-hidden">
<TextCursor :key="key" :text="text" :follow-mouse-direction="followMouseDirection"
:random-float="randomFloat" />
<div
class="absolute inset-0 flex items-center justify-center pointer-events-none text-[4rem] font-[900] text-[#222] select-none">
Hover Around!
</div>
</div>
<Customize>
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Text</label>
<input v-model="text" type="text" placeholder="Enter text..." maxlength="10"
class="w-[160px] px-3 py-2 bg-[#0b0b0b] border border-[#333] rounded-md text-white focus:outline-none focus:border-[#666]" />
</div>
<PreviewSwitch title="Follow Mouse Direction" v-model="followMouseDirection"
@update:model-value="forceRerender" />
<PreviewSwitch title="Enable Random Floating" v-model="randomFloat" @update:model-value="forceRerender" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['motion-v']" />
</template>
<template #code>
<CodeExample :code-object="textCursor" />
</template>
<template #cli>
<CliInstallation :command="textCursor.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import Dependencies from '../../components/code/Dependencies.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import TextCursor from '../../content/TextAnimations/TextCursor/TextCursor.vue'
import { textCursor } from '@/constants/code/TextAnimations/textCursorCode'
import { useForceRerender } from '@/composables/useForceRerender'
const { rerenderKey: key, forceRerender } = useForceRerender()
const text = ref('💚')
const followMouseDirection = ref(true)
const randomFloat = ref(true)
const propData = [
{
name: 'text',
type: 'string',
default: '⚛️',
description: 'The text string to display as the trail.'
},
{
name: 'delay',
type: 'number',
default: '0.01',
description: 'The entry stagger delay in seconds for the fade-out animation.'
},
{
name: 'spacing',
type: 'number',
default: '100',
description: 'The spacing in pixels between each trail point.'
},
{
name: 'followMouseDirection',
type: 'boolean',
default: 'true',
description: 'If true, each text rotates to follow the mouse direction.'
},
{
name: 'randomFloat',
type: 'boolean',
default: 'true',
description: 'If true, enables random floating offsets in position and rotation for a dynamic effect.'
},
{
name: 'exitDuration',
type: 'number',
default: '0.5',
description: 'The duration in seconds for the exit animation of each trail item.'
},
{
name: 'removalInterval',
type: 'number',
default: '30',
description: 'The interval in milliseconds between removing trail items when the mouse stops moving.'
},
{
name: 'maxPoints',
type: 'number',
default: '5',
description: 'The maximum number of trail points to display.'
}
]
</script>

View File

@@ -0,0 +1,176 @@
<template>
<div class="text-pressure-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative bg-[#060010] min-h-[400px] max-h-[450px] overflow-hidden mb-6">
<div class="w-full h-full">
<TextPressure :key="rerenderKey" :text="text" :flex="flex" :alpha="alpha" :stroke="stroke" :width="width"
:weight="weight" :italic="italic" :text-color="textColor" :stroke-color="strokeColor"
:min-font-size="36" />
</div>
</div>
<Customize>
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Text</label>
<input v-model="text" type="text" placeholder="Your text here..." maxlength="10"
class="w-[200px] px-3 py-2 bg-[#0b0b0b] border border-[#333] rounded-md text-white focus:outline-none focus:border-[#666]" />
</div>
<div class="color-controls">
<PreviewColor title="Text Color" v-model="textColor" @update:model-value="forceRerender" />
<PreviewColor title="Stroke Color" v-model="strokeColor" @update:model-value="forceRerender" />
</div>
<p class="mt-6 text-[#999] text-sm">Animation Settings</p>
<div class="flex gap-4 flex-wrap">
<PreviewSwitch title="Flex" :model-value="flex"
@update:model-value="(val: boolean) => { flex = val; forceRerender() }" />
<PreviewSwitch title="Alpha" :model-value="alpha"
@update:model-value="(val: boolean) => { alpha = val; forceRerender() }" />
<PreviewSwitch title="Stroke" :model-value="stroke"
@update:model-value="(val: boolean) => { stroke = val; forceRerender() }" />
<PreviewSwitch title="Width" :model-value="width"
@update:model-value="(val: boolean) => { width = val; forceRerender() }" />
<PreviewSwitch title="Weight" :model-value="weight"
@update:model-value="(val: boolean) => { weight = val; forceRerender() }" />
<PreviewSwitch title="Italic" :model-value="italic"
@update:model-value="(val: boolean) => { italic = val; forceRerender() }" />
</div>
</Customize>
<PropTable :data="propData" />
</template>
<template #code>
<CodeExample :code-object="textPressure" />
</template>
<template #cli>
<CliInstallation :command="textPressure.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import PreviewColor from '../../components/common/PreviewColor.vue'
import TextPressure from '../../content/TextAnimations/TextPressure/TextPressure.vue'
import { textPressure } from '@/constants/code/TextAnimations/textPressureCode'
import { useForceRerender } from '@/composables/useForceRerender'
const text = ref('Hello!')
const flex = ref(true)
const alpha = ref(false)
const stroke = ref(false)
const width = ref(true)
const weight = ref(true)
const italic = ref(true)
const textColor = ref('#ffffff')
const strokeColor = ref('#27FF64')
const { rerenderKey, forceRerender } = useForceRerender()
const propData = [
{
name: 'text',
type: 'string',
default: '"Hello!"',
description: 'Text content that will be displayed and animated.'
},
{
name: 'fontFamily',
type: 'string',
default: 'Compressa VF',
description: 'Name of the variable font family.'
},
{
name: 'fontUrl',
type: 'string',
default: 'URL to a .woff2 or .ttf file',
description: 'URL for the variable font file (needed)'
},
{
name: 'flex',
type: 'boolean',
default: 'true',
description: 'Whether the characters are spaced using flex layout.'
},
{
name: 'scale',
type: 'boolean',
default: 'false',
description: 'If true, vertically scales the text to fill its container height.'
},
{
name: 'alpha',
type: 'boolean',
default: 'false',
description: 'If true, applies an opacity effect based on cursor distance.'
},
{
name: 'stroke',
type: 'boolean',
default: 'false',
description: 'If true, adds a stroke effect around characters.'
},
{
name: 'width',
type: 'boolean',
default: 'true',
description: 'If true, varies the variable-font "width" axis.'
},
{
name: 'weight',
type: 'boolean',
default: 'true',
description: 'If true, varies the variable-font "weight" axis.'
},
{
name: 'italic',
type: 'boolean',
default: 'true',
description: 'If true, varies the variable-font "italics" axis.'
},
{
name: 'textColor',
type: 'string',
default: '#FFFFFF',
description: 'The fill color of the text'
},
{
name: 'strokeColor',
type: 'string',
default: '#FF0000',
description: 'The stroke color that will be applied to the text when "stroke" is set to true'
},
{
name: 'className',
type: 'string',
default: "''",
description: 'Additional class for styling the <h1> wrapper.'
},
{
name: 'minFontSize',
type: 'number',
default: '24',
description: 'Sets a minimum font-size to avoid overly tiny text on smaller screens.'
}
]
</script>
<style scoped>
.color-controls {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<div class="text-trail-demo">
<TabbedLayout>
<template #preview>
<div class="demo-container relative h-[500px] overflow-hidden p-0">
<TextTrail :key="`${key}-${animateColor}`" :noise-factor="noiseFactor" :noise-scale="noiseScale / 10000"
:font-weight="fontWeight" :alpha-persist-factor="alphaPersistFactor" :animate-color="animateColor"
:text-color="animateColor ? undefined : '#ffffff'" />
</div>
<Customize>
<PreviewSlider title="Noise Factor" v-model="noiseFactor" :min="1" :max="25" :step="1"
@update:model-value="forceRerender" />
<PreviewSlider title="Noise Scale" v-model="noiseScale" :min="0" :max="100" :step="1"
@update:model-value="forceRerender" />
<PreviewSlider title="Font Weight" v-model="fontWeight" :min="100" :max="900" :step="100"
@update:model-value="forceRerender" />
<PreviewSlider title="Alpha Persist Factor" v-model="alphaPersistFactor" :min="0.5" :max="0.95" :step="0.01"
@update:model-value="forceRerender" />
<PreviewSwitch title="Animate Color" v-model="animateColor" @update:model-value="forceRerender" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['three']" />
</template>
<template #code>
<CodeExample :code-object="textTrail" />
</template>
<template #cli>
<CliInstallation :command="textTrail.cli" />
</template>
</TabbedLayout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TabbedLayout from '../../components/common/TabbedLayout.vue'
import PropTable from '../../components/common/PropTable.vue'
import Dependencies from '../../components/code/Dependencies.vue'
import CliInstallation from '../../components/code/CliInstallation.vue'
import CodeExample from '../../components/code/CodeExample.vue'
import Customize from '../../components/common/Customize.vue'
import PreviewSlider from '../../components/common/PreviewSlider.vue'
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
import TextTrail from '../../content/TextAnimations/TextTrail/TextTrail.vue'
import { textTrail } from '@/constants/code/TextAnimations/textTrailCode'
import { useForceRerender } from '@/composables/useForceRerender'
const { rerenderKey: key, forceRerender } = useForceRerender()
const noiseFactor = ref(1)
const noiseScale = ref(5)
const fontWeight = ref(900)
const alphaPersistFactor = ref(0.95)
const animateColor = ref(false)
const propData = [
{
name: 'text',
type: 'string',
default: '"Vibe"',
description: 'The text to display with the trail effect'
},
{
name: 'fontFamily',
type: 'string',
default: '"Figtree"',
description: 'Font family for the text'
},
{
name: 'fontWeight',
type: 'string | number',
default: '"900"',
description: 'Font weight for the text'
},
{
name: 'noiseFactor',
type: 'number',
default: '1',
description: 'Controls the intensity of the noise effect'
},
{
name: 'noiseScale',
type: 'number',
default: '0.0005',
description: 'Scale factor for the noise distortion (very small values like 0.0005)'
},
{
name: 'rgbPersistFactor',
type: 'number',
default: '0.98',
description: 'RGB persistence factor for the trail effect (0-1)'
},
{
name: 'alphaPersistFactor',
type: 'number',
default: '0.95',
description: 'Alpha persistence factor for the trail effect (0-1)'
},
{
name: 'animateColor',
type: 'boolean',
default: 'false',
description: 'Whether to animate color changes over time'
},
{
name: 'startColor',
type: 'string',
default: '"#ffffff"',
description: 'Starting color for the text (hex format)'
},
{
name: 'textColor',
type: 'string',
default: '"#ffffff"',
description: 'Static color for the text (hex format)'
},
{
name: 'backgroundColor',
type: 'number | string',
default: '0x271e37',
description: 'Background color (hex number or string)'
},
{
name: 'colorCycleInterval',
type: 'number',
default: '3000',
description: 'Interval in milliseconds for color cycling when animateColor is true'
},
{
name: 'supersample',
type: 'number',
default: '2',
description: 'Supersampling factor for text quality (higher = better quality)'
}
]
</script>
<style scoped>
.demo-container {
padding: 0;
}
</style>