mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-09 08:29:30 -06:00
Component Boom
This commit is contained in:
197
src/demo/Animations/AnimatedContentDemo.vue
Normal file
197
src/demo/Animations/AnimatedContentDemo.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="animated-content-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative py-6 overflow-hidden">
|
||||
<RefreshButton @click="forceRerender" />
|
||||
|
||||
<div :key="key" class="flex justify-center items-center h-96">
|
||||
<AnimatedContent :direction="direction" :delay="delay" :distance="distance" :reverse="reverse"
|
||||
:duration="duration" :ease="ease" :initial-opacity="initialOpacity" :animate-opacity="animateOpacity"
|
||||
:scale="scale" :threshold="threshold" @complete="() => console.log('✅ Animation Complete!')">
|
||||
<div class="demo-content">
|
||||
<h4>Animated Content</h4>
|
||||
<p>It will animate in when it enters the viewport.</p>
|
||||
</div>
|
||||
</AnimatedContent>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSelect title="Animation Direction" v-model="direction" :options="directionOptions"
|
||||
@update:model-value="(val) => { direction = val as 'vertical' | 'horizontal'; forceRerender(); }" />
|
||||
|
||||
<PreviewSelect title="Easing Function" v-model="ease" :options="easeOptions"
|
||||
@update:model-value="(val) => { ease = val as string; forceRerender(); }" />
|
||||
|
||||
<PreviewSlider title="Distance" v-model="distance" :min="50" :max="300" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Duration" v-model="duration" :min="0.1" :max="3" :step="0.1" value-unit="s"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Delay" v-model="delay" :min="0" :max="2" :step="0.1" value-unit="s"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Initial Opacity" v-model="initialOpacity" :min="0" :max="1" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Initial Scale" v-model="scale" :min="0.1" :max="2" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Threshold" v-model="threshold" :min="0.1" :max="1" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Reverse Direction" v-model="reverse" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Animate Opacity" v-model="animateOpacity" @update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="animatedContent" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="animatedContent.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 PreviewSelect from '../../components/common/PreviewSelect.vue'
|
||||
import RefreshButton from '../../components/common/RefreshButton.vue'
|
||||
import AnimatedContent from '../../content/Animations/AnimatedContent/AnimatedContent.vue'
|
||||
import { animatedContent } from '@/constants/code/Animations/animatedContentCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const { rerenderKey: key, forceRerender } = useForceRerender()
|
||||
|
||||
const direction = ref<'vertical' | 'horizontal'>('vertical')
|
||||
const distance = ref(100)
|
||||
const delay = ref(0)
|
||||
const reverse = ref(false)
|
||||
const duration = ref(0.8)
|
||||
const ease = ref('power3.out')
|
||||
const initialOpacity = ref(0)
|
||||
const animateOpacity = ref(true)
|
||||
const scale = ref(1)
|
||||
const threshold = ref(0.1)
|
||||
|
||||
const directionOptions = [
|
||||
{ label: 'Vertical', value: 'vertical' },
|
||||
{ label: 'Horizontal', value: 'horizontal' }
|
||||
]
|
||||
|
||||
const easeOptions = [
|
||||
{ label: 'Power3 Out', value: 'power3.out' },
|
||||
{ label: 'Bounce Out', value: 'bounce.out' },
|
||||
{ label: 'Elastic Out', value: 'elastic.out(1, 0.3)' }
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'distance',
|
||||
type: 'number',
|
||||
default: '100',
|
||||
description: 'Distance (in pixels) the component moves during animation.'
|
||||
},
|
||||
{
|
||||
name: 'direction',
|
||||
type: '"vertical" | "horizontal"',
|
||||
default: '"vertical"',
|
||||
description: 'Animation direction. Can be "vertical" or "horizontal".'
|
||||
},
|
||||
{
|
||||
name: 'reverse',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Whether the animation moves in the reverse direction.'
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'number',
|
||||
default: '0.8',
|
||||
description: 'Duration of the animation in seconds.'
|
||||
},
|
||||
{
|
||||
name: 'ease',
|
||||
type: 'string | function',
|
||||
default: '"power3.out"',
|
||||
description: 'GSAP easing function for the animation.'
|
||||
},
|
||||
{
|
||||
name: 'initialOpacity',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'Initial opacity before animation begins.'
|
||||
},
|
||||
{
|
||||
name: 'animateOpacity',
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description: 'Whether to animate opacity during transition.'
|
||||
},
|
||||
{
|
||||
name: 'scale',
|
||||
type: 'number',
|
||||
default: '1',
|
||||
description: 'Initial scale of the component.'
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
type: 'number',
|
||||
default: '0.1',
|
||||
description: 'Intersection threshold to trigger animation (0-1).'
|
||||
},
|
||||
{
|
||||
name: 'delay',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'Delay before animation starts (in seconds).'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Additional CSS classes for styling.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-content {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
border: 1px solid #ffffff1c;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.demo-content h4 {
|
||||
color: #fff;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-content p {
|
||||
color: #a1a1aa;
|
||||
text-align: center;
|
||||
max-width: 25ch;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
116
src/demo/Animations/ClickSparkDemo.vue
Normal file
116
src/demo/Animations/ClickSparkDemo.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="click-spark-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<ClickSpark :key="rerenderKey" :spark-color="sparkColor" :spark-size="sparkSize" :spark-radius="sparkRadius"
|
||||
:spark-count="sparkCount" :duration="duration" :easing="easing" :extra-scale="extraScale"
|
||||
class="click-spark-demo-area">
|
||||
</ClickSpark>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 flex items-center justify-center pointer-events-none text-[4rem] font-[900] text-[#222] select-none">
|
||||
Click Around!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewColor title="Spark Color" v-model="sparkColor" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Spark Size" v-model="sparkSize" :min="5" :max="30" :step="1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Spark Radius" v-model="sparkRadius" :min="10" :max="50" :step="5"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Spark Count" v-model="sparkCount" :min="4" :max="20" :step="1"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Duration (ms)" v-model="duration" :min="200" :max="1000" :step="50"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Extra Scale" v-model="extraScale" :min="0.5" :max="2" :step="0.1"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="clickSpark" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="clickSpark.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 PreviewColor from '../../components/common/PreviewColor.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import ClickSpark from '../../content/Animations/ClickSpark/ClickSpark.vue'
|
||||
import { clickSpark } from '@/constants/code/Animations/clickSparkCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const sparkColor = ref('#ffffff')
|
||||
const sparkSize = ref(10)
|
||||
const sparkRadius = ref(15)
|
||||
const sparkCount = ref(8)
|
||||
const duration = ref(400)
|
||||
const easing = ref<"linear" | "ease-in" | "ease-out" | "ease-in-out">('ease-out')
|
||||
const extraScale = ref(1)
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{ name: 'sparkColor', type: 'string', default: "'#fff'", description: 'Color of the spark lines.' },
|
||||
{ name: 'sparkSize', type: 'number', default: '10', description: 'Length of each spark line.' },
|
||||
{ name: 'sparkRadius', type: 'number', default: '15', description: 'Distance sparks travel from the click center.' },
|
||||
{ name: 'sparkCount', type: 'number', default: '8', description: 'Number of spark lines per click.' },
|
||||
{ name: 'duration', type: 'number', default: '400', description: 'Animation duration in milliseconds.' },
|
||||
{ name: 'easing', type: 'string', default: "'ease-out'", description: 'Easing function: "linear", "ease-in", "ease-out", or "ease-in-out".' },
|
||||
{ name: 'extraScale', type: 'number', default: '1.0', description: 'Scale multiplier for spark distance and size.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.click-spark-demo-area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.demo-text {
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-text h3 {
|
||||
font-size: 1.2rem;
|
||||
color: #fff;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.demo-text p {
|
||||
font-size: 0.9rem;
|
||||
color: #999;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
132
src/demo/Animations/CountUpDemo.vue
Normal file
132
src/demo/Animations/CountUpDemo.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="count-up-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<h2 class="demo-title-extra">Default</h2>
|
||||
<div class="demo-container relative">
|
||||
<CountUp :key="keyDefault" :from="0" :to="100" separator="," direction="up" :duration="1"
|
||||
class-name="count-up-text" />
|
||||
|
||||
<RefreshButton @click="forceRerenderDefault" />
|
||||
</div>
|
||||
|
||||
<h2 class="demo-title-extra">Start Programatically</h2>
|
||||
<div class="demo-container flex flex-col justify-center items-center relative min-h-[200px]">
|
||||
<button class="bg-[#0b0b0b] cursor-pointer rounded-[10px] border border-[#222] text-white px-4 py-2 mb-4"
|
||||
@click="setStartCounting(true)">
|
||||
Count to 500!
|
||||
</button>
|
||||
|
||||
<CountUp :key="keyProgramatically" :from="100" :to="500" :start-when="startCounting" :duration="5"
|
||||
class-name="count-up-text" />
|
||||
|
||||
<RefreshButton v-if="startCounting" @click="forceRerenderProgramatically" />
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="countup" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="countup.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 RefreshButton from '../../components/common/RefreshButton.vue'
|
||||
import CountUp from '../../content/Animations/CountUp/CountUp.vue'
|
||||
import { countup } from '@/constants/code/Animations/countUpCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const startCounting = ref(false)
|
||||
|
||||
const { rerenderKey: keyDefault, forceRerender: forceRerenderDefault } = useForceRerender()
|
||||
const { rerenderKey: keyProgramatically, forceRerender: forceRerenderProgramatically } = useForceRerender()
|
||||
|
||||
const setStartCounting = (value: boolean) => {
|
||||
startCounting.value = value
|
||||
if (value) {
|
||||
forceRerenderProgramatically()
|
||||
}
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'to',
|
||||
type: 'number',
|
||||
default: '—',
|
||||
description: 'The target number to count up to.'
|
||||
},
|
||||
{
|
||||
name: 'from',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'The initial number from which the count starts.'
|
||||
},
|
||||
{
|
||||
name: 'direction',
|
||||
type: 'string',
|
||||
default: '"up"',
|
||||
description: 'Direction of the count; can be "up" or "down". When this is set to "down", "from" and "to" become reversed, in order to count down.'
|
||||
},
|
||||
{
|
||||
name: 'delay',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'Delay in seconds before the counting starts.'
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'number',
|
||||
default: '2',
|
||||
description: 'Duration of the count animation - based on the damping and stiffness configured inside the component.'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'CSS class to apply to the component for additional styling.'
|
||||
},
|
||||
{
|
||||
name: 'startWhen',
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description: 'A boolean to control whether the animation should start when the component is in view. It basically works like an if statement, if this is true, the count will start.'
|
||||
},
|
||||
{
|
||||
name: 'separator',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Character to use as a thousands separator in the displayed number.'
|
||||
},
|
||||
{
|
||||
name: 'onStart',
|
||||
type: 'function',
|
||||
default: '—',
|
||||
description: 'Callback function that is called when the count animation starts.'
|
||||
},
|
||||
{
|
||||
name: 'onEnd',
|
||||
type: 'function',
|
||||
default: '—',
|
||||
description: 'Callback function that is called when the count animation ends.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
min-height: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
151
src/demo/Animations/CubesDemo.vue
Normal file
151
src/demo/Animations/CubesDemo.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="relative demo-container h-[650px] overflow-hidden">
|
||||
<Cubes :borderStyle="borderStyle" :gridSize="gridSize" :maxAngle="maxAngle" :radius="radius"
|
||||
:autoAnimate="autoAnimate" :rippleOnClick="rippleOnClick" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSelect title="Border Preference" :options="borderOptions" v-model="borderStyle" :width="150" />
|
||||
|
||||
<PreviewSlider title="Grid Size" :min="6" :max="12" :step="1" v-model="gridSize" :width="150" />
|
||||
|
||||
<PreviewSlider title="Max Angle" :min="15" :max="180" :step="5" v-model="maxAngle" valueUnit="°" :width="150" />
|
||||
|
||||
<PreviewSlider title="Radius" :min="1" :max="5" :step="1" v-model="radius" :width="150" />
|
||||
|
||||
<PreviewSwitch title="Auto Animate" v-model="autoAnimate" />
|
||||
|
||||
<PreviewSwitch title="Ripple On Click" v-model="rippleOnClick" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependencyList="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :codeObject="cubes" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation v-bind="cubes" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSelect from '../../components/common/PreviewSelect.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import Dependencies from '../../components/code/Dependencies.vue'
|
||||
|
||||
import { cubes } from '../../constants/code/Animations/cubesCode'
|
||||
import Cubes from '../../content/Animations/Cubes/Cubes.vue'
|
||||
|
||||
const borderStyle = ref("2px dashed #A7EF9E")
|
||||
const gridSize = ref(10)
|
||||
const maxAngle = ref(45)
|
||||
const radius = ref(3)
|
||||
const autoAnimate = ref(true)
|
||||
const rippleOnClick = ref(true)
|
||||
|
||||
const borderOptions = [
|
||||
{ value: "2px dotted #fff", label: "Dotted White" },
|
||||
{ value: "2px dashed #A7EF9E", label: "Dashed Green" },
|
||||
{ value: "3px solid #fff", label: "Solid White" }
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "gridSize",
|
||||
type: "number",
|
||||
default: "10",
|
||||
description: "The size of the grid (number of cubes per row/column)"
|
||||
},
|
||||
{
|
||||
name: "cubeSize",
|
||||
type: "number",
|
||||
default: "undefined",
|
||||
description: "Fixed size of each cube in pixels. If not provided, cubes will be responsive"
|
||||
},
|
||||
{
|
||||
name: "maxAngle",
|
||||
type: "number",
|
||||
default: "45",
|
||||
description: "Maximum rotation angle for the tilt effect in degrees"
|
||||
},
|
||||
{
|
||||
name: "radius",
|
||||
type: "number",
|
||||
default: "3",
|
||||
description: "Radius of the tilt effect (how many cubes around the cursor are affected)"
|
||||
},
|
||||
{
|
||||
name: "easing",
|
||||
type: "string",
|
||||
default: "'power3.out'",
|
||||
description: "GSAP easing function for the tilt animation"
|
||||
},
|
||||
{
|
||||
name: "duration",
|
||||
type: "object",
|
||||
default: "{ enter: 0.3, leave: 0.6 }",
|
||||
description: "Animation duration for enter and leave effects"
|
||||
},
|
||||
{
|
||||
name: "cellGap",
|
||||
type: "number | object",
|
||||
default: "undefined",
|
||||
description: "Gap between cubes. Can be a number or object with row/col properties"
|
||||
},
|
||||
{
|
||||
name: "borderStyle",
|
||||
type: "string",
|
||||
default: "'1px solid #fff'",
|
||||
description: "CSS border style for cube faces"
|
||||
},
|
||||
{
|
||||
name: "faceColor",
|
||||
type: "string",
|
||||
default: "'#060010'",
|
||||
description: "Background color for cube faces"
|
||||
},
|
||||
{
|
||||
name: "shadow",
|
||||
type: "boolean | string",
|
||||
default: "false",
|
||||
description: "Shadow effect for cubes. Can be boolean or custom CSS shadow"
|
||||
},
|
||||
{
|
||||
name: "autoAnimate",
|
||||
type: "boolean",
|
||||
default: "true",
|
||||
description: "Whether to automatically animate when user is idle"
|
||||
},
|
||||
{
|
||||
name: "rippleOnClick",
|
||||
type: "boolean",
|
||||
default: "true",
|
||||
description: "Whether to show ripple effect on click"
|
||||
},
|
||||
{
|
||||
name: "rippleColor",
|
||||
type: "string",
|
||||
default: "'#fff'",
|
||||
description: "Color of the ripple effect"
|
||||
},
|
||||
{
|
||||
name: "rippleSpeed",
|
||||
type: "number",
|
||||
default: "2",
|
||||
description: "Speed multiplier for the ripple animation"
|
||||
}
|
||||
]
|
||||
</script>
|
||||
143
src/demo/Animations/GlareHoverDemo.vue
Normal file
143
src/demo/Animations/GlareHoverDemo.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="glare-hover-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative h-[600px] overflow-hidden">
|
||||
<div class="flex justify-center items-center h-full">
|
||||
<GlareHover background="#111" border-color="#222" border-radius="20px" width="400px" height="300px"
|
||||
:glare-color="glareColor" :glare-opacity="glareOpacity" :glare-size="glareSize"
|
||||
:transition-duration="transitionDuration" :play-once="playOnce">
|
||||
<div class="text-center text-5xl font-black text-[#222] m-0">
|
||||
Hover Me
|
||||
</div>
|
||||
</GlareHover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewColor title="Glare Color" v-model="glareColor" />
|
||||
|
||||
<PreviewSlider title="Glare Opacity" v-model="glareOpacity" :min="0" :max="1" :step="0.1" />
|
||||
|
||||
<PreviewSlider title="Glare Size" v-model="glareSize" :min="100" :max="500" :step="25" value-unit="%" />
|
||||
|
||||
<PreviewSlider title="Transition Duration" v-model="transitionDuration" :min="200" :max="2000" :step="50"
|
||||
value-unit="ms" />
|
||||
|
||||
<PreviewSwitch title="Play Once" v-model="playOnce" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="glareHover" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="glareHover.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 PreviewColor from '../../components/common/PreviewColor.vue'
|
||||
import GlareHover from '../../content/Animations/GlareHover/GlareHover.vue'
|
||||
import { glareHover } from '@/constants/code/Animations/glareHoverCode'
|
||||
|
||||
const glareColor = ref('#ffffff')
|
||||
const glareOpacity = ref(0.3)
|
||||
const glareSize = ref(300)
|
||||
const transitionDuration = ref(800)
|
||||
const playOnce = ref(false)
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'width',
|
||||
type: 'string',
|
||||
default: '500px',
|
||||
description: 'The width of the hover element.'
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'string',
|
||||
default: '500px',
|
||||
description: 'The height of the hover element.'
|
||||
},
|
||||
{
|
||||
name: 'background',
|
||||
type: 'string',
|
||||
default: '#000',
|
||||
description: 'The background color of the element.'
|
||||
},
|
||||
{
|
||||
name: 'borderRadius',
|
||||
type: 'string',
|
||||
default: '10px',
|
||||
description: 'The border radius of the element.'
|
||||
},
|
||||
{
|
||||
name: 'borderColor',
|
||||
type: 'string',
|
||||
default: '#333',
|
||||
description: 'The border color of the element.'
|
||||
},
|
||||
{
|
||||
name: 'glareColor',
|
||||
type: 'string',
|
||||
default: '#ffffff',
|
||||
description: 'The color of the glare effect (hex format).'
|
||||
},
|
||||
{
|
||||
name: 'glareOpacity',
|
||||
type: 'number',
|
||||
default: '0.5',
|
||||
description: 'The opacity of the glare effect (0-1).'
|
||||
},
|
||||
{
|
||||
name: 'glareAngle',
|
||||
type: 'number',
|
||||
default: '-45',
|
||||
description: 'The angle of the glare effect in degrees.'
|
||||
},
|
||||
{
|
||||
name: 'glareSize',
|
||||
type: 'number',
|
||||
default: '250',
|
||||
description: 'The size of the glare effect as a percentage (e.g. 250 = 250%).'
|
||||
},
|
||||
{
|
||||
name: 'transitionDuration',
|
||||
type: 'number',
|
||||
default: '650',
|
||||
description: 'The duration of the transition in milliseconds.'
|
||||
},
|
||||
{
|
||||
name: 'playOnce',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'If true, the glare only animates on hover and doesn\'t return on mouse leave.'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Additional CSS class names.'
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
type: 'object',
|
||||
default: '{}',
|
||||
description: 'Additional inline styles.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
155
src/demo/Animations/MagnetDemo.vue
Normal file
155
src/demo/Animations/MagnetDemo.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="magnet-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<h2 class="demo-title-extra">Container</h2>
|
||||
<div class="demo-container">
|
||||
<Magnet :key="rerenderKey" :padding="padding" :disabled="disabled" :magnetStrength="magnetStrength">
|
||||
<div class="magnet-container">
|
||||
Hover Me!
|
||||
</div>
|
||||
</Magnet>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 class="demo-title-extra">Link</h2>
|
||||
<div class="demo-container">
|
||||
<Magnet :key="rerenderKey + 1" :padding="Math.floor(padding / 2)" :disabled="disabled"
|
||||
:magnetStrength="magnetStrength">
|
||||
<a href="https://github.com/DavidHDev/vue-bits" target="_blank" rel="noreferrer" class="magnet-link">
|
||||
Star <span class="accent">Vue Bits</span> on GitHub!
|
||||
</a>
|
||||
</Magnet>
|
||||
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Disabled" v-model="disabled" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Padding" v-model="padding" :min="0" :max="300" :step="10" value-unit="px"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Strength" v-model="magnetStrength" :min="1" :max="10" :step="1"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="magnet" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="magnet.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 PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import Magnet from '../../content/Animations/Magnet/Magnet.vue'
|
||||
import { magnet } from '@/constants/code/Animations/magnetCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const disabled = ref(false)
|
||||
const padding = ref(100)
|
||||
const magnetStrength = ref(2)
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'padding',
|
||||
type: 'number',
|
||||
default: '100',
|
||||
description: 'Specifies the distance (in pixels) around the element that activates the magnet pull.',
|
||||
},
|
||||
{
|
||||
name: 'disabled',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Disables the magnet effect when set to true.',
|
||||
},
|
||||
{
|
||||
name: 'magnetStrength',
|
||||
type: 'number',
|
||||
default: '2',
|
||||
description: 'Controls the strength of the pull; higher values reduce movement, lower values increase it.',
|
||||
},
|
||||
{
|
||||
name: 'activeTransition',
|
||||
type: 'string',
|
||||
default: '"transform 0.3s ease-out"',
|
||||
description: 'CSS transition applied to the element when the magnet is active.',
|
||||
},
|
||||
{
|
||||
name: 'inactiveTransition',
|
||||
type: 'string',
|
||||
default: '"transform 0.5s ease-in-out"',
|
||||
description: 'CSS transition applied when the magnet is inactive (mouse out of range).',
|
||||
},
|
||||
{
|
||||
name: 'wrapperClassName',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Optional CSS class name for the outermost wrapper element.',
|
||||
},
|
||||
{
|
||||
name: 'innerClassName',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Optional CSS class name for the moving (inner) element.',
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-title-extra {
|
||||
font-size: 1.1rem;
|
||||
color: #fff;
|
||||
margin: 2rem 0 1rem 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
position: relative;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.magnet-container {
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background: #111;
|
||||
border: 1px solid #222;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.magnet-link {
|
||||
font-size: 1.125rem;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.magnet-link:hover {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.accent {
|
||||
color: #27ff56;
|
||||
}
|
||||
</style>
|
||||
93
src/demo/Animations/MagnetLinesDemo.vue
Normal file
93
src/demo/Animations/MagnetLinesDemo.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="magnet-lines-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container overflow-hidden flex justify-center pb-4 items-center">
|
||||
<MagnetLines
|
||||
:rows="10"
|
||||
:columns="12"
|
||||
container-size="40vmin"
|
||||
line-width="2px"
|
||||
line-height="30px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="magnetLines" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="magnetLines.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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 MagnetLines from '../../content/Animations/MagnetLines/MagnetLines.vue'
|
||||
import { magnetLines } from '@/constants/code/Animations/magnetLinesCode'
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'rows',
|
||||
type: 'number',
|
||||
default: '9',
|
||||
description: 'Number of grid rows.'
|
||||
},
|
||||
{
|
||||
name: 'columns',
|
||||
type: 'number',
|
||||
default: '9',
|
||||
description: 'Number of grid columns.'
|
||||
},
|
||||
{
|
||||
name: 'containerSize',
|
||||
type: 'string',
|
||||
default: '80vmin',
|
||||
description: 'Specifies the width and height of the entire grid container.'
|
||||
},
|
||||
{
|
||||
name: 'lineColor',
|
||||
type: 'string',
|
||||
default: '#efefef',
|
||||
description: 'Color for each line (the <span> elements).'
|
||||
},
|
||||
{
|
||||
name: 'lineWidth',
|
||||
type: 'string',
|
||||
default: '1vmin',
|
||||
description: "Specifies each line's thickness."
|
||||
},
|
||||
{
|
||||
name: 'lineHeight',
|
||||
type: 'string',
|
||||
default: '6vmin',
|
||||
description: "Specifies each line's length."
|
||||
},
|
||||
{
|
||||
name: 'baseAngle',
|
||||
type: 'number',
|
||||
default: '-10',
|
||||
description: 'Initial rotation angle (in degrees) before pointer movement.'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Additional class name(s) applied to the container.'
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
type: 'object',
|
||||
default: '{}',
|
||||
description: 'Inline styles for the container.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
122
src/demo/Animations/PixelTransitionDemo.vue
Normal file
122
src/demo/Animations/PixelTransitionDemo.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="pixel-transition-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div
|
||||
class="demo-container flex flex-col items-center justify-center min-h-[400px] max-h-[400px] relative overflow-hidden">
|
||||
<PixelTransition :key="key" :grid-size="gridSize" :pixel-color="pixelColor"
|
||||
:animation-step-duration="animationStepDuration" class-name="custom-pixel-card">
|
||||
<template #firstContent>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"
|
||||
alt="Default" style="width: 100%; height: 100%; object-fit: cover;" />
|
||||
</template>
|
||||
<template #secondContent>
|
||||
<div style="width: 100%; height: 100%; display: grid; place-items: center; background-color: #111;">
|
||||
<p style="font-weight: 900; font-size: 3rem; color: #fff;">Meow!</p>
|
||||
</div>
|
||||
</template>
|
||||
</PixelTransition>
|
||||
<div class="mt-2 text-[#a6a6a6]">Psst, hover the card!</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Grid Size" v-model="gridSize" :min="2" :max="50" :step="1"
|
||||
@update:model-value="forceRerender" width="200" />
|
||||
|
||||
<PreviewSlider title="Animation Duration" v-model="animationStepDuration" :min="0.1" :max="2" :step="0.1"
|
||||
value-unit="s" @update:model-value="forceRerender" width="200" />
|
||||
|
||||
<PreviewColor title="Pixel Color" v-model="pixelColor" @update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="pixelTransition" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="pixelTransition.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 PixelTransition from '../../content/Animations/PixelTransition/PixelTransition.vue'
|
||||
import { pixelTransition } from '@/constants/code/Animations/pixelTransitionCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
import PreviewColor from '../../components/common/PreviewColor.vue'
|
||||
|
||||
const { rerenderKey: key, forceRerender } = useForceRerender()
|
||||
const gridSize = ref(8)
|
||||
const pixelColor = ref('#ffffff')
|
||||
const animationStepDuration = ref(0.4)
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'firstContent',
|
||||
type: 'VNode | string',
|
||||
default: '—',
|
||||
description: 'Content to show by default (e.g., an <img> or text).'
|
||||
},
|
||||
{
|
||||
name: 'secondContent',
|
||||
type: 'VNode | string',
|
||||
default: '—',
|
||||
description: 'Content revealed upon hover or click.'
|
||||
},
|
||||
{
|
||||
name: 'gridSize',
|
||||
type: 'number',
|
||||
default: '7',
|
||||
description: 'Number of rows/columns in the pixel grid.'
|
||||
},
|
||||
{
|
||||
name: 'pixelColor',
|
||||
type: 'string',
|
||||
default: 'currentColor',
|
||||
description: 'Background color used for each pixel block.'
|
||||
},
|
||||
{
|
||||
name: 'animationStepDuration',
|
||||
type: 'number',
|
||||
default: '0.3',
|
||||
description: 'Length of the pixel reveal/hide in seconds.'
|
||||
},
|
||||
{
|
||||
name: 'aspectRatio',
|
||||
type: 'string',
|
||||
default: '"100%"',
|
||||
description: "Sets the 'padding-top' (or aspect-ratio) for the container."
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '—',
|
||||
description: 'Optional additional class names for styling.'
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
type: 'object',
|
||||
default: '{}',
|
||||
description: 'Optional inline styles for the container.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-pixel-card {
|
||||
box-shadow: 0 2px 16px 0 #00000033;
|
||||
}
|
||||
</style>
|
||||
90
src/demo/Backgrounds/AuroraDemo.vue
Normal file
90
src/demo/Backgrounds/AuroraDemo.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="aurora-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<Aurora :color-stops="colorStops" :amplitude="amplitude" :blend="blend" :speed="speed"
|
||||
:intensity="intensity" class="w-full h-96" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<div class="space-y-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
Color Stops
|
||||
</h3>
|
||||
<div class="flex gap-4">
|
||||
<PreviewColor v-for="(color, index) in colorStops" :key="index" :title="`Color ${index + 1}`"
|
||||
:model-value="color" @update:model-value="(value) => updateColorStop(index, value)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PreviewSlider title="Amplitude" :model-value="amplitude" @update:model-value="amplitude = $event" :min="0"
|
||||
:max="2" :step="0.1" />
|
||||
|
||||
<PreviewSlider title="Blend" :model-value="blend" @update:model-value="blend = $event" :min="0" :max="1"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Speed" :model-value="speed" @update:model-value="speed = $event" :min="0" :max="3"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Intensity" :model-value="intensity" @update:model-value="intensity = $event" :min="0"
|
||||
:max="2" :step="0.1" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="aurora" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="aurora.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 Aurora from '@/content/Backgrounds/Aurora/Aurora.vue'
|
||||
import PreviewColor from '@/components/common/PreviewColor.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import { aurora } from '@/constants/code/Backgrounds/auroraCode'
|
||||
|
||||
const colorStops = ref(['#171D22', '#7cff67', '#171D22'])
|
||||
const amplitude = ref(1.0)
|
||||
const blend = ref(0.5)
|
||||
const speed = ref(1.0)
|
||||
const intensity = ref(1.0)
|
||||
|
||||
|
||||
const updateColorStop = (index: number, color: string) => {
|
||||
colorStops.value[index] = color
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{ name: 'colorStops', type: 'string[]', default: "['#171D22', '#7cff67', '#171D22']", description: 'Array of color stops for the aurora gradient.' },
|
||||
{ name: 'amplitude', type: 'number', default: '1.0', description: 'Controls the height variation of the aurora effect.' },
|
||||
{ name: 'blend', type: 'number', default: '0.5', description: 'Controls the blending/smoothness of the aurora effect.' },
|
||||
{ name: 'speed', type: 'number', default: '1.0', description: 'Controls the animation speed of the aurora effect.' },
|
||||
{ name: 'intensity', type: 'number', default: '1.0', description: 'Controls the overall intensity/opacity of the aurora effect.' },
|
||||
{ name: 'time', type: 'number', default: 'undefined', description: 'Optional time override for the animation.' },
|
||||
{ name: 'className', type: 'string', default: '""', description: 'Additional CSS class names for styling.' },
|
||||
{ name: 'style', type: 'CSSProperties', default: '{}', description: 'Inline styles for the component.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -3,8 +3,6 @@
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="height: 500px; overflow: hidden;">
|
||||
<RefreshButton @refresh="forceRerender" />
|
||||
|
||||
<DotGrid :key="rerenderKey" :dot-size="dotSize" :gap="gap" :base-color="baseColor" :active-color="activeColor"
|
||||
:proximity="proximity" :speed-trigger="speedTrigger" :shock-radius="shockRadius"
|
||||
:shock-strength="shockStrength" :max-speed="maxSpeed" :resistance="resistance"
|
||||
@@ -13,14 +11,8 @@
|
||||
|
||||
<Customize>
|
||||
<div class="color-controls">
|
||||
<div class="color-input">
|
||||
<label>Base Color</label>
|
||||
<input type="color" v-model="baseColor" @change="forceRerender" />
|
||||
</div>
|
||||
<div class="color-input">
|
||||
<label>Active Color</label>
|
||||
<input type="color" v-model="activeColor" @change="forceRerender" />
|
||||
</div>
|
||||
<PreviewColor title="Base Color" v-model="baseColor" @update:model-value="forceRerender" />
|
||||
<PreviewColor title="Active Color" v-model="activeColor" @update:model-value="forceRerender" />
|
||||
</div>
|
||||
|
||||
<PreviewSlider title="Dot Size" v-model="dotSize" :min="2" :max="50" :step="1"
|
||||
@@ -68,13 +60,13 @@
|
||||
<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 PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import PreviewColor from '../../components/common/PreviewColor.vue'
|
||||
import DotGrid from '../../content/Backgrounds/DotGrid/DotGrid.vue'
|
||||
import { dotGrid } from '@/constants/code/Backgrounds/dotGridCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
@@ -120,34 +112,4 @@ const propData = [
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.color-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.color-input label {
|
||||
font-size: 0.875rem;
|
||||
color: #a1a1aa;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.color-input input[type="color"] {
|
||||
width: 50px;
|
||||
height: 32px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.color-input input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.color-input input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
91
src/demo/Backgrounds/IridescenceDemo.vue
Normal file
91
src/demo/Backgrounds/IridescenceDemo.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="relative demo-container h-[500px] p-0 overflow-hidden">
|
||||
<Iridescence :key="key" :speed="speed" :color="colors" :mouseReact="mouseInteraction" :amplitude="amplitude" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider :min="0" :max="1" :step="0.1" v-model="colors[0]" title="Red" />
|
||||
|
||||
<PreviewSlider :min="0" :max="1" :step="0.1" v-model="colors[1]" title="Green" />
|
||||
|
||||
<PreviewSlider :min="0" :max="1" :step="0.1" v-model="colors[2]" title="Blue" />
|
||||
|
||||
<PreviewSlider :min="0" :max="2" :step="0.1" v-model="speed" title="Speed" @update:modelValue="forceRerender" />
|
||||
|
||||
<PreviewSlider :min="0" :max="0.5" :step="0.01" v-model="amplitude" title="Amplitude"
|
||||
@update:modelValue="forceRerender" />
|
||||
|
||||
<PreviewSwitch v-model="mouseInteraction" title="Enable Mouse Interaction" @update:modelValue="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependencyList="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :codeObject="iridescence" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation v-bind="iridescence" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import Dependencies from '../../components/code/Dependencies.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import Iridescence from '../../content/Backgrounds/Iridescence/Iridescence.vue'
|
||||
import { iridescence } from '../../constants/code/Backgrounds/iridescenceCode'
|
||||
import { useForceRerender } from '../../composables/useForceRerender'
|
||||
|
||||
const colors = ref<[number, number, number]>([1, 1, 1])
|
||||
const speed = ref(1)
|
||||
const amplitude = ref(0.1)
|
||||
const mouseInteraction = ref(true)
|
||||
|
||||
const { rerenderKey: key, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "color",
|
||||
type: "Array<number>",
|
||||
default: "[1, 1, 1]",
|
||||
description: "Base color as an array of RGB values (each between 0 and 1)."
|
||||
},
|
||||
{
|
||||
name: "speed",
|
||||
type: "number",
|
||||
default: "1.0",
|
||||
description: "Speed multiplier for the animation."
|
||||
},
|
||||
{
|
||||
name: "amplitude",
|
||||
type: "number",
|
||||
default: "0.1",
|
||||
description: "Amplitude for the mouse-driven effect."
|
||||
},
|
||||
{
|
||||
name: "mouseReact",
|
||||
type: "boolean",
|
||||
default: "true",
|
||||
description: "Enable or disable mouse interaction with the shader."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
128
src/demo/Backgrounds/LetterGlitchDemo.vue
Normal file
128
src/demo/Backgrounds/LetterGlitchDemo.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="letter-glitch-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<LetterGlitch
|
||||
:key="rerenderKey"
|
||||
:glitch-colors="colors"
|
||||
:glitch-speed="speed"
|
||||
:center-vignette="showCenterVignette"
|
||||
:outer-vignette="showOuterVignette"
|
||||
:smooth="smooth"
|
||||
class="w-full h-96"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<button
|
||||
@click="randomizeColors"
|
||||
class="px-3 py-2 text-xs bg-[#111] hover:bg-[#222] text-white rounded-lg border border-[#333] transition-colors"
|
||||
>
|
||||
Randomize Colors
|
||||
</button>
|
||||
|
||||
<PreviewSlider
|
||||
title="Glitch Speed"
|
||||
:model-value="speed"
|
||||
@update:model-value="speed = $event"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="5"
|
||||
/>
|
||||
|
||||
<PreviewSwitch
|
||||
title="Smooth Animation"
|
||||
:model-value="smooth"
|
||||
@update:model-value="updateSmooth"
|
||||
/>
|
||||
|
||||
<PreviewSwitch
|
||||
title="Show Center Vignette"
|
||||
:model-value="showCenterVignette"
|
||||
@update:model-value="updateCenterVignette"
|
||||
/>
|
||||
|
||||
<PreviewSwitch
|
||||
title="Show Outer Vignette"
|
||||
:model-value="showOuterVignette"
|
||||
@update:model-value="updateOuterVignette"
|
||||
/>
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="[]" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="letterGlitch" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="letterGlitch.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 LetterGlitch from '@/content/Backgrounds/LetterGlitch/LetterGlitch.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import PreviewSwitch from '@/components/common/PreviewSwitch.vue'
|
||||
import { letterGlitch } from '@/constants/code/Backgrounds/letterGlitchCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const smooth = ref(true)
|
||||
const speed = ref(10)
|
||||
const colors = ref(['#2b4539', '#61dca3', '#61b3dc'])
|
||||
const showCenterVignette = ref(true)
|
||||
const showOuterVignette = ref(false)
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const randomHex = () => {
|
||||
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')
|
||||
}
|
||||
|
||||
const randomizeColors = () => {
|
||||
colors.value = [randomHex(), randomHex(), randomHex()]
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const updateSmooth = (value: boolean) => {
|
||||
smooth.value = value
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const updateCenterVignette = (value: boolean) => {
|
||||
showCenterVignette.value = value
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const updateOuterVignette = (value: boolean) => {
|
||||
showOuterVignette.value = value
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{ name: 'glitchColors', type: 'string[]', default: "['#2b4539', '#61dca3', '#61b3dc']", description: 'Controls the colors of the letters rendered in the canvas.' },
|
||||
{ name: 'glitchSpeed', type: 'number', default: '50', description: 'Controls the speed at which letters scramble in the animation.' },
|
||||
{ name: 'centerVignette', type: 'boolean', default: 'false', description: 'When true, renders a radial gradient in the center of the container' },
|
||||
{ name: 'outerVignette', type: 'boolean', default: 'true', description: 'When true, renders an inner radial gradient around the edges of the container.' },
|
||||
{ name: 'smooth', type: 'boolean', default: 'true', description: 'When true, smoothens the animation of the letters for a more subtle feel.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
74
src/demo/Backgrounds/LightningDemo.vue
Normal file
74
src/demo/Backgrounds/LightningDemo.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="lightning-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<Lightning :hue="hue" :x-offset="xOffset" :speed="speed" :intensity="intensity"
|
||||
:size="size" class="w-full h-96" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Hue" :model-value="hue" @update:model-value="hue = $event" :min="0" :max="360"
|
||||
:step="1" />
|
||||
|
||||
<PreviewSlider title="X Offset" :model-value="xOffset" @update:model-value="xOffset = $event" :min="-2"
|
||||
:max="2" :step="0.1" />
|
||||
|
||||
<PreviewSlider title="Speed" :model-value="speed" @update:model-value="speed = $event" :min="0.5" :max="2"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Intensity" :model-value="intensity" @update:model-value="intensity = $event" :min="0.1"
|
||||
:max="2" :step="0.1" />
|
||||
|
||||
<PreviewSlider title="Size" :model-value="size" @update:model-value="size = $event" :min="0.1" :max="3"
|
||||
:step="0.1" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="[]" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="lightning" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="lightning.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 Lightning from '@/content/Backgrounds/Lightning/Lightning.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import { lightning } from '@/constants/code/Backgrounds/lightningCode'
|
||||
|
||||
const hue = ref(160)
|
||||
const xOffset = ref(0)
|
||||
const speed = ref(1)
|
||||
const intensity = ref(1)
|
||||
const size = ref(1)
|
||||
|
||||
const propData = [
|
||||
{ name: 'hue', type: 'number', default: '230', description: 'Hue of the lightning in degrees (0 to 360).' },
|
||||
{ name: 'xOffset', type: 'number', default: '0', description: 'Horizontal offset of the lightning in normalized units.' },
|
||||
{ name: 'speed', type: 'number', default: '1', description: 'Animation speed multiplier for the lightning.' },
|
||||
{ name: 'intensity', type: 'number', default: '1', description: 'Brightness multiplier for the lightning.' },
|
||||
{ name: 'size', type: 'number', default: '1', description: 'Scale factor for the bolt size.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
105
src/demo/Backgrounds/ParticlesDemo.vue
Normal file
105
src/demo/Backgrounds/ParticlesDemo.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="particles-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<Particles :key="rerenderKey" :particle-colors="[color]" :particle-count="particleCount"
|
||||
:particle-spread="particleSpread" :speed="speed" :particle-base-size="baseSize"
|
||||
:move-particles-on-hover="moveParticlesOnHover" :alpha-particles="alphaParticles"
|
||||
:disable-rotation="disableRotation" class="w-full h-96" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<div class="flex gap-4 items-center">
|
||||
<PreviewColor title="Color" :model-value="color" @update:model-value="updateColor" />
|
||||
</div>
|
||||
|
||||
<PreviewSlider title="Count" :model-value="particleCount" @update:model-value="particleCount = $event"
|
||||
:min="100" :max="1000" :step="100" />
|
||||
|
||||
<PreviewSlider title="Spread" :model-value="particleSpread" @update:model-value="particleSpread = $event"
|
||||
:min="10" :max="100" :step="10" />
|
||||
|
||||
<PreviewSlider title="Speed" :model-value="speed" @update:model-value="speed = $event" :min="0" :max="2"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Base Size" :model-value="baseSize" @update:model-value="baseSize = $event" :min="100"
|
||||
:max="1000" :step="100" />
|
||||
|
||||
<PreviewSwitch title="Mouse Interaction" :model-value="moveParticlesOnHover"
|
||||
@update:model-value="moveParticlesOnHover = $event" />
|
||||
|
||||
<PreviewSwitch title="Particle Transparency" :model-value="alphaParticles"
|
||||
@update:model-value="alphaParticles = $event" />
|
||||
|
||||
<PreviewSwitch title="Disable Rotation" :model-value="disableRotation"
|
||||
@update:model-value="disableRotation = $event" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="particles" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="particles.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 Particles from '@/content/Backgrounds/Particles/Particles.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import PreviewSwitch from '@/components/common/PreviewSwitch.vue'
|
||||
import PreviewColor from '@/components/common/PreviewColor.vue'
|
||||
import { particles } from '@/constants/code/Backgrounds/particlesCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const color = ref('#ffffff')
|
||||
const particleCount = ref(200)
|
||||
const particleSpread = ref(10)
|
||||
const speed = ref(0.1)
|
||||
const baseSize = ref(100)
|
||||
const moveParticlesOnHover = ref(true)
|
||||
const alphaParticles = ref(false)
|
||||
const disableRotation = ref(false)
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const updateColor = (value: string) => {
|
||||
color.value = value
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{ name: 'particleCount', type: 'number', default: '200', description: 'The number of particles to generate.' },
|
||||
{ name: 'particleSpread', type: 'number', default: '10', description: 'Controls how far particles are spread from the center.' },
|
||||
{ name: 'speed', type: 'number', default: '0.1', description: 'Speed factor controlling the animation pace.' },
|
||||
{ name: 'particleColors', type: 'string[]', default: "['#ffffff']", description: 'An array of hex color strings used to color the particles.' },
|
||||
{ name: 'moveParticlesOnHover', type: 'boolean', default: 'false', description: 'Determines if particles should move in response to mouse hover.' },
|
||||
{ name: 'particleHoverFactor', type: 'number', default: '1', description: 'Multiplier for the particle movement when hovering.' },
|
||||
{ name: 'alphaParticles', type: 'boolean', default: 'false', description: 'If true, particles are rendered with varying transparency; otherwise, as solid circles.' },
|
||||
{ name: 'particleBaseSize', type: 'number', default: '100', description: 'The base size of the particles.' },
|
||||
{ name: 'sizeRandomness', type: 'number', default: '1', description: 'Controls the variation in particle sizes (0 means all particles have the same size).' },
|
||||
{ name: 'cameraDistance', type: 'number', default: '20', description: 'Distance from the camera to the particle system.' },
|
||||
{ name: 'disableRotation', type: 'boolean', default: 'false', description: 'If true, stops the particle system from rotating.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
76
src/demo/Backgrounds/SilkDemo.vue
Normal file
76
src/demo/Backgrounds/SilkDemo.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="silk-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<Silk :speed="speed" :scale="scale" :color="color" :noise-intensity="noiseIntensity" :rotation="rotation"
|
||||
class="w-full h-96" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Speed" :model-value="speed" @update:model-value="speed = $event" :min="0" :max="10"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Scale" :model-value="scale" @update:model-value="scale = $event" :min="0.1" :max="3"
|
||||
:step="0.1" />
|
||||
|
||||
<PreviewSlider title="Noise Intensity" :model-value="noiseIntensity"
|
||||
@update:model-value="noiseIntensity = $event" :min="0" :max="3" :step="0.1" />
|
||||
|
||||
<PreviewSlider title="Rotation" :model-value="rotation" @update:model-value="rotation = $event" :min="0"
|
||||
:max="6.28" :step="0.1" />
|
||||
|
||||
<PreviewColor title="Color" :model-value="color" @update:model-value="color = $event" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="silk" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="silk.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 Silk from '@/content/Backgrounds/Silk/Silk.vue'
|
||||
import PreviewColor from '@/components/common/PreviewColor.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import { silk } from '@/constants/code/Backgrounds/silkCode'
|
||||
|
||||
const speed = ref(5)
|
||||
const scale = ref(1)
|
||||
const color = ref('#7B7481')
|
||||
const noiseIntensity = ref(1.5)
|
||||
const rotation = ref(0)
|
||||
|
||||
const propData = [
|
||||
{ name: 'speed', type: 'number', default: '5', description: 'Animation speed multiplier' },
|
||||
{ name: 'scale', type: 'number', default: '1', description: 'Scale factor for the pattern' },
|
||||
{ name: 'color', type: 'string', default: "'#7B7481'", description: 'Base color of the silk pattern' },
|
||||
{ name: 'noiseIntensity', type: 'number', default: '1.5', description: 'Intensity of the noise effect' },
|
||||
{ name: 'rotation', type: 'number', default: '0', description: 'Rotation angle in radians' },
|
||||
{ name: 'className', type: 'string', default: '""', description: 'Additional CSS class names for styling.' },
|
||||
{ name: 'style', type: 'CSSProperties', default: '{}', description: 'Inline styles for the component.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
116
src/demo/Backgrounds/SquaresDemo.vue
Normal file
116
src/demo/Backgrounds/SquaresDemo.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="relative min-h-[200px] demo-container overflow-hidden p-0">
|
||||
<div class="w-full h-[500px] overflow-hidden">
|
||||
<Squares :squareSize="size" :speed="speed" :direction="direction" :borderColor="borderColor"
|
||||
:hoverFillColor="hoverColor" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<div class="flex items-center mb-6">
|
||||
<span class="text-sm mr-2">Direction</span>
|
||||
<div class="flex">
|
||||
<button v-for="dir in directions" :key="dir.value" @click="direction = dir.value" :class="[
|
||||
'px-3 py-2 text-xs h-8 text-white border-r border-[#333] cursor-pointer last:border-r-0',
|
||||
direction === dir.value
|
||||
? 'bg-[#222] hover:bg-[#222]'
|
||||
: 'bg-[#111] hover:bg-[#111]'
|
||||
]" class="first:rounded-l last:rounded-r">
|
||||
{{ dir.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PreviewSlider :min="10" :max="100" :step="1" v-model="size" title="Square Size" />
|
||||
|
||||
<PreviewSlider :min="0.1" :max="2" :step="0.01" v-model="speed" title="Animation Speed" />
|
||||
|
||||
<div class="flex gap-4">
|
||||
<PreviewColor v-model="borderColor" title="Border Color" />
|
||||
|
||||
<PreviewColor v-model="hoverColor" title="Hover Color" />
|
||||
</div>
|
||||
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :codeObject="squares" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation v-bind="squares" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import PreviewColor from '../../components/common/PreviewColor.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import Squares from '../../content/Backgrounds/Squares/Squares.vue'
|
||||
import { squares } from '../../constants/code/Backgrounds/squaresCode'
|
||||
|
||||
const direction = ref<'diagonal' | 'up' | 'right' | 'down' | 'left'>('diagonal')
|
||||
const borderColor = ref('#333')
|
||||
const hoverColor = ref('#27FF64')
|
||||
const size = ref(40)
|
||||
const speed = ref(0.5)
|
||||
|
||||
const directions = [
|
||||
{ value: 'diagonal', label: 'Diagonal' },
|
||||
{ value: 'up', label: 'Up' },
|
||||
{ value: 'right', label: 'Right' },
|
||||
{ value: 'down', label: 'Down' },
|
||||
{ value: 'left', label: 'Left' }
|
||||
] as const
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "direction",
|
||||
type: "string",
|
||||
default: "'right'",
|
||||
description: "Direction of square animation. Options: 'diagonal', 'up', 'right', 'down', 'left'."
|
||||
},
|
||||
{
|
||||
name: "speed",
|
||||
type: "number",
|
||||
default: "1",
|
||||
description: "Animation speed multiplier."
|
||||
},
|
||||
{
|
||||
name: "borderColor",
|
||||
type: "string",
|
||||
default: "'#999'",
|
||||
description: "Color of the square borders."
|
||||
},
|
||||
{
|
||||
name: "squareSize",
|
||||
type: "number",
|
||||
default: "40",
|
||||
description: "Size of individual squares in pixels."
|
||||
},
|
||||
{
|
||||
name: "hoverFillColor",
|
||||
type: "string",
|
||||
default: "'#222'",
|
||||
description: "Fill color when hovering over squares."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
81
src/demo/Backgrounds/ThreadsDemo.vue
Normal file
81
src/demo/Backgrounds/ThreadsDemo.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="relative demo-container h-[500px] overflow-hidden p-0">
|
||||
<Threads :amplitude="amplitude" :distance="distance" :enableMouseInteraction="enableMouseInteraction"
|
||||
:color="[1, 1, 1]" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider :min="0" :max="5" :step="0.1" v-model="amplitude" title="Amplitude" />
|
||||
|
||||
<PreviewSlider :min="0" :max="2" :step="0.1" v-model="distance" title="Distance" />
|
||||
|
||||
<PreviewSwitch v-model="enableMouseInteraction" title="Enable Mouse Interaction" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependencyList="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :codeObject="threads" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="threads.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import TabbedLayout from '../../components/common/TabbedLayout.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import PropTable from '../../components/common/PropTable.vue'
|
||||
import Dependencies from '../../components/code/Dependencies.vue'
|
||||
import CodeExample from '../../components/code/CodeExample.vue'
|
||||
import CliInstallation from '../../components/code/CliInstallation.vue'
|
||||
import Threads from '../../content/Backgrounds/Threads/Threads.vue'
|
||||
import { threads } from '../../constants/code/Backgrounds/threadsCode'
|
||||
|
||||
const amplitude = ref(1)
|
||||
const distance = ref(0)
|
||||
const enableMouseInteraction = ref(true)
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "color",
|
||||
type: "[number, number, number]",
|
||||
default: "[1, 1, 1]",
|
||||
description: "Customizes the color of the lines (RGB)."
|
||||
},
|
||||
{
|
||||
name: "amplitude",
|
||||
type: "number",
|
||||
default: "1",
|
||||
description: "Adjusts the intensity of the wave effect on the lines."
|
||||
},
|
||||
{
|
||||
name: "distance",
|
||||
type: "number",
|
||||
default: "0",
|
||||
description: "Controls the spacing between the lines. A value of 0 means no offset."
|
||||
},
|
||||
{
|
||||
name: "enableMouseInteraction",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description: "Enables smooth mouse hover effects that modulate the line's movement and amplitude."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
71
src/demo/Backgrounds/WavesDemo.vue
Normal file
71
src/demo/Backgrounds/WavesDemo.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="waves-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container">
|
||||
<Waves :wave-speed-x="waveSpeedX" :line-color="color" class="w-full h-96" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Wave Speed X" :model-value="waveSpeedX" @update:model-value="waveSpeedX = $event"
|
||||
:min="0" :max="0.1" :step="0.01" />
|
||||
|
||||
<div class="flex gap-4 items-center">
|
||||
<PreviewColor title="Waves Color" :model-value="color" @update:model-value="color = $event" />
|
||||
</div>
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="[]" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="waves" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="waves.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 Waves from '@/content/Backgrounds/Waves/Waves.vue'
|
||||
import PreviewSlider from '@/components/common/PreviewSlider.vue'
|
||||
import PreviewColor from '@/components/common/PreviewColor.vue'
|
||||
import { waves } from '@/constants/code/Backgrounds/wavesCode'
|
||||
|
||||
const color = ref('#ffffff')
|
||||
const waveSpeedX = ref(0.0125)
|
||||
|
||||
const propData = [
|
||||
{ name: 'lineColor', type: 'string', default: 'black', description: 'Defines the color of the wave lines drawn on the canvas.' },
|
||||
{ name: 'backgroundColor', type: 'string', default: 'transparent', description: 'Sets the background color of the waves container.' },
|
||||
{ name: 'waveSpeedX', type: 'number', default: '0.0125', description: 'Horizontal speed factor for the wave animation.' },
|
||||
{ name: 'waveSpeedY', type: 'number', default: '0.005', description: 'Vertical speed factor for the wave animation.' },
|
||||
{ name: 'waveAmpX', type: 'number', default: '32', description: 'Horizontal amplitude of each wave.' },
|
||||
{ name: 'waveAmpY', type: 'number', default: '16', description: 'Vertical amplitude of each wave.' },
|
||||
{ name: 'xGap', type: 'number', default: '10', description: 'Horizontal gap between individual wave lines.' },
|
||||
{ name: 'yGap', type: 'number', default: '32', description: 'Vertical gap between points on each wave line.' },
|
||||
{ name: 'friction', type: 'number', default: '0.925', description: 'Controls how quickly the cursor effect slows down.' },
|
||||
{ name: 'tension', type: 'number', default: '0.005', description: 'Determines the "springiness" of the cursor effect on points.' },
|
||||
{ name: 'maxCursorMove', type: 'number', default: '100', description: 'Limits how far each point can shift due to cursor movement.' },
|
||||
{ name: 'style', type: 'object', default: '{}', description: 'Inline styles applied to the container element.' },
|
||||
{ name: 'className', type: 'string', default: '""', description: 'Custom class name(s) applied to the container element.' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
196
src/demo/Components/CardSwapDemo.vue
Normal file
196
src/demo/Components/CardSwapDemo.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="card-swap-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container h-[500px] overflow-hidden flex flex-col lg:flex-row relative">
|
||||
<div
|
||||
class="w-full lg:w-1/2 h-auto lg:h-full flex flex-col justify-center items-center lg:items-start text-center lg:text-left pt-8 lg:pt-0 pb-4 lg:pb-0 px-4 lg:px-4">
|
||||
<h2 class="text-2xl md:text-3xl lg:text-4xl mb-4 font-medium leading-tight pl-0 lg:pl-24">
|
||||
Card stacks have never
|
||||
<span class="inline lg:block">looked so good</span>
|
||||
</h2>
|
||||
<p class="text-lg lg:text-xl mb-4 font-normal leading-tight text-gray-400 pl-0 lg:pl-24">
|
||||
Just look at it go!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full lg:w-1/2 h-96 lg:h-full relative">
|
||||
<CardSwap :key="rerenderKey" :width="500" :height="400" :card-distance="cardDistance"
|
||||
:vertical-distance="verticalDistance" :delay="delay" :skew-amount="skewAmount" :easing="easing"
|
||||
:pause-on-hover="pauseOnHover" @card-click="handleCardClick">
|
||||
<template #card-0>
|
||||
<div class="border-b border-white bg-gradient-to-t from-[#222] to-[#0b0b0b]">
|
||||
<div class="m-2 flex items-center">
|
||||
<i class="pi pi-circle-fill mr-2"></i>
|
||||
<span>Smooth</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative p-2">
|
||||
<video autoplay loop muted playsinline class="rounded-[15px] w-full h-full">
|
||||
<source
|
||||
src="https://cdn.dribbble.com/userupload/7053861/file/original-7956be57144058795db6bb24875bdab9.mp4"
|
||||
type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #card-1>
|
||||
<div class="border-b border-white bg-gradient-to-t from-[#222] to-[#0b0b0b]">
|
||||
<div class="m-2 flex items-center">
|
||||
<i class="pi pi-code mr-2"></i>
|
||||
<span>Reliable</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative p-2">
|
||||
<video autoplay loop muted playsinline class="rounded-[15px] w-full h-full">
|
||||
<source
|
||||
src="https://cdn.dribbble.com/userupload/7078020/file/original-b071e9063d9e3ba86a85a61b9d5a7c42.mp4"
|
||||
type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #card-2>
|
||||
<div class="border-b border-white bg-gradient-to-t from-[#222] to-[#0b0b0b]">
|
||||
<div class="m-2 flex items-center">
|
||||
<i class="pi pi-sliders-h mr-2"></i>
|
||||
<span>Customizable</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative p-2">
|
||||
<video autoplay loop muted playsinline class="rounded-[15px] w-full h-full">
|
||||
<source
|
||||
src="https://cdn.dribbble.com/userupload/7098541/file/original-0b063b12ca835421580e6034368ad95a.mp4"
|
||||
type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
</CardSwap>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Pause On Hover" v-model="pauseOnHover" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Card Distance" v-model="cardDistance" :min="30" :max="100" :step="5" />
|
||||
|
||||
<PreviewSlider title="Vertical Distance" v-model="verticalDistance" :min="40" :max="120" :step="5" />
|
||||
|
||||
<PreviewSlider title="Delay (ms)" v-model="delay" :min="3000" :max="8000" :step="500" />
|
||||
|
||||
<PreviewSlider title="Skew Amount" v-model="skewAmount" :min="0" :max="12" :step="1" />
|
||||
|
||||
<button
|
||||
class="text-xs bg-[#111] rounded-[10px] border border-[#333] hover:bg-[#222] text-white h-8 px-3 transition-colors"
|
||||
@click="toggleEasing">
|
||||
Easing: <span class="text-zinc-400"> {{ easing }}</span>
|
||||
</button>
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="cardSwap" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="cardSwap.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 CardSwap from '../../content/Components/CardSwap/CardSwap.vue'
|
||||
import { cardSwap } from '@/constants/code/Components/cardSwapCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const cardDistance = ref(60)
|
||||
const verticalDistance = ref(70)
|
||||
const delay = ref(5000)
|
||||
const skewAmount = ref(6)
|
||||
const easing = ref<'elastic' | 'linear'>('elastic')
|
||||
const pauseOnHover = ref(false)
|
||||
|
||||
const toggleEasing = () => {
|
||||
easing.value = easing.value === 'elastic' ? 'linear' : 'elastic'
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const handleCardClick = (index: number) => {
|
||||
console.log(`Card ${index} clicked`)
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "width",
|
||||
type: "number | string",
|
||||
default: "500",
|
||||
description: "Width of the card container"
|
||||
},
|
||||
{
|
||||
name: "height",
|
||||
type: "number | string",
|
||||
default: "400",
|
||||
description: "Height of the card container"
|
||||
},
|
||||
{
|
||||
name: "cardDistance",
|
||||
type: "number",
|
||||
default: "60",
|
||||
description: "X-axis spacing between cards"
|
||||
},
|
||||
{
|
||||
name: "verticalDistance",
|
||||
type: "number",
|
||||
default: "70",
|
||||
description: "Y-axis spacing between cards"
|
||||
},
|
||||
{
|
||||
name: "delay",
|
||||
type: "number",
|
||||
default: "5000",
|
||||
description: "Milliseconds between card swaps"
|
||||
},
|
||||
{
|
||||
name: "pauseOnHover",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description: "Whether to pause animation on hover"
|
||||
},
|
||||
{
|
||||
name: "onCardClick",
|
||||
type: "(idx: number) => void",
|
||||
default: "undefined",
|
||||
description: "Callback function when a card is clicked"
|
||||
},
|
||||
{
|
||||
name: "skewAmount",
|
||||
type: "number",
|
||||
default: "6",
|
||||
description: "Degree of slope for top/bottom edges"
|
||||
},
|
||||
{
|
||||
name: "easing",
|
||||
type: "'linear' | 'elastic'",
|
||||
default: "'elastic'",
|
||||
description: "Animation easing type"
|
||||
}
|
||||
]
|
||||
</script>
|
||||
116
src/demo/Components/CarouselDemo.vue
Normal file
116
src/demo/Components/CarouselDemo.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="carousel-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative h-[500px] overflow-hidden flex items-center justify-center">
|
||||
<Carousel :key="rerenderKey" :base-width="width" :autoplay="autoplay" :autoplay-delay="autoplayDelay"
|
||||
:pause-on-hover="pauseOnHover" :loop="loop" :round="round" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Width" v-model="width" :min="250" :max="330" :step="10"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Round Variant" v-model="round" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Loop" v-model="loop" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Autoplay" v-model="autoplay" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Delay" v-model="autoplayDelay" :min="1000" :max="4000" :step="1000"
|
||||
:disabled="!autoplay" @update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSwitch title="Pause On Hover" v-model="pauseOnHover" :disabled="!autoplay"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['motion-v']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="carousel" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="carousel.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 PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import Carousel from '../../content/Components/Carousel/Carousel.vue'
|
||||
import { carousel } from '@/constants/code/Components/carouselCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const width = ref(300)
|
||||
const autoplay = ref(false)
|
||||
const autoplayDelay = ref(3000)
|
||||
const pauseOnHover = ref(false)
|
||||
const loop = ref(false)
|
||||
const round = ref(false)
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "items",
|
||||
type: "CarouselItem[]",
|
||||
default: "DEFAULT_ITEMS",
|
||||
description:
|
||||
"An array of carousel items. Each item must include title, description, id, and icon."
|
||||
},
|
||||
{
|
||||
name: "baseWidth",
|
||||
type: "number",
|
||||
default: "300",
|
||||
description:
|
||||
"Total width (in px) of the carousel container. Effective item width is baseWidth minus padding."
|
||||
},
|
||||
{
|
||||
name: "autoplay",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description:
|
||||
"Enables automatic scrolling to the next item at a fixed interval."
|
||||
},
|
||||
{
|
||||
name: "autoplayDelay",
|
||||
type: "number",
|
||||
default: "3000",
|
||||
description:
|
||||
"Delay in milliseconds between automatic scrolls when autoplay is enabled."
|
||||
},
|
||||
{
|
||||
name: "pauseOnHover",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description:
|
||||
"Pauses the autoplay functionality when the carousel is hovered."
|
||||
},
|
||||
{
|
||||
name: "loop",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description:
|
||||
"When true, the carousel loops seamlessly from the last item back to the first."
|
||||
},
|
||||
{
|
||||
name: "round",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description:
|
||||
"When true, the carousel is rendered with a 1:1 aspect ratio and circular container/items."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
63
src/demo/Components/DecayCardDemo.vue
Normal file
63
src/demo/Components/DecayCardDemo.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="decay-card-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="overflow: hidden;">
|
||||
<DecayCard>
|
||||
<div class="text-[2rem]">
|
||||
Decay<br />Card
|
||||
</div>
|
||||
</DecayCard>
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="decayCard" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="decayCard.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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 DecayCard from '../../content/Components/DecayCard/DecayCard.vue'
|
||||
import { decayCard } from '@/constants/code/Components/decayCardCode'
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'children',
|
||||
type: 'Slot',
|
||||
default: '',
|
||||
description: 'The content to be rendered inside the card.'
|
||||
},
|
||||
{
|
||||
name: 'width',
|
||||
type: 'number',
|
||||
default: '300',
|
||||
description: 'The width of the card in pixels.'
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'number',
|
||||
default: '400',
|
||||
description: 'The height of the card in pixels.'
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'string',
|
||||
default: 'https://picsum.photos/300/400?grayscale',
|
||||
description: 'Allows setting the background image of the card.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
164
src/demo/Components/DockDemo.vue
Normal file
164
src/demo/Components/DockDemo.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="dock-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="min-height: 400px; position: relative;">
|
||||
<div class="demo-content">
|
||||
<Dock :key="rerenderKey" :items="items" :panel-height="panelHeight" :base-item-size="baseItemSize"
|
||||
:magnification="magnification" :distance="200" :dock-height="256"
|
||||
:spring="{ mass: 0.1, stiffness: 150, damping: 12 }" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 flex items-center justify-center pointer-events-none text-[4rem] font-[900] text-[#222] select-none">
|
||||
Try It!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Background Height" :min="30" :max="200" :step="10" :model-value="panelHeight"
|
||||
@update:model-value="(val: number) => { panelHeight = val; forceRerender(); }" />
|
||||
|
||||
<PreviewSlider title="Item Size" :min="20" :max="60" :step="10" :model-value="baseItemSize"
|
||||
@update:model-value="(val: number) => { baseItemSize = val; forceRerender(); }" />
|
||||
|
||||
<PreviewSlider title="Magnification" :min="50" :max="100" :step="10" :model-value="magnification"
|
||||
@update:model-value="(val: number) => { magnification = val; forceRerender(); }" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['motion-v']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="dock" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="dock.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, h } 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 Dependencies from '../../components/code/Dependencies.vue'
|
||||
import Customize from '../../components/common/Customize.vue'
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue'
|
||||
import Dock from '../../content/Components/Dock/Dock.vue'
|
||||
import { dock } from '@/constants/code/Components/dockCode'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const panelHeight = ref(68)
|
||||
const baseItemSize = ref(50)
|
||||
const magnification = ref(70)
|
||||
|
||||
const toast = useToast()
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const HomeIcon = () => h('i', { class: 'pi pi-home', style: { fontSize: '18px', color: 'white' } })
|
||||
const ArchiveIcon = () => h('i', { class: 'pi pi-inbox', style: { fontSize: '18px', color: 'white' } })
|
||||
const ProfileIcon = () => h('i', { class: 'pi pi-user', style: { fontSize: '18px', color: 'white' } })
|
||||
const SettingsIcon = () => h('i', { class: 'pi pi-cog', style: { fontSize: '18px', color: 'white' } })
|
||||
|
||||
const showAlert = (message: string) => {
|
||||
toast.add({
|
||||
severity: 'secondary',
|
||||
summary: `${message} button was clicked`,
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
|
||||
const items = [
|
||||
{ icon: HomeIcon, label: 'Home', onClick: () => showAlert('Home') },
|
||||
{ icon: ArchiveIcon, label: 'Archive', onClick: () => showAlert('Archive') },
|
||||
{ icon: ProfileIcon, label: 'Profile', onClick: () => showAlert('Profile') },
|
||||
{ icon: SettingsIcon, label: 'Settings', onClick: () => showAlert('Settings') },
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "items",
|
||||
type: "DockItemData[]",
|
||||
default: "[]",
|
||||
description: "Array of dock items. Each item should include an icon, label, onClick handler, and an optional className."
|
||||
},
|
||||
{
|
||||
name: "className",
|
||||
type: "string",
|
||||
default: '""',
|
||||
description: "Additional CSS classes for the dock panel."
|
||||
},
|
||||
{
|
||||
name: "distance",
|
||||
type: "number",
|
||||
default: "200",
|
||||
description: "Pixel distance used to calculate the magnification effect based on mouse proximity."
|
||||
},
|
||||
{
|
||||
name: "panelHeight",
|
||||
type: "number",
|
||||
default: "68",
|
||||
description: "Height (in pixels) of the dock panel."
|
||||
},
|
||||
{
|
||||
name: "baseItemSize",
|
||||
type: "number",
|
||||
default: "50",
|
||||
description: "The base size (in pixels) for each dock item."
|
||||
},
|
||||
{
|
||||
name: "dockHeight",
|
||||
type: "number",
|
||||
default: "256",
|
||||
description: "Maximum height (in pixels) of the dock container."
|
||||
},
|
||||
{
|
||||
name: "magnification",
|
||||
type: "number",
|
||||
default: "70",
|
||||
description: "The magnified size (in pixels) applied to a dock item when hovered."
|
||||
},
|
||||
{
|
||||
name: "spring",
|
||||
type: "SpringOptions",
|
||||
default: "{ mass: 0.1, stiffness: 150, damping: 12 }",
|
||||
description: "Configuration options for the spring animation."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 900;
|
||||
color: #271E37;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
119
src/demo/Components/ElasticSliderDemo.vue
Normal file
119
src/demo/Components/ElasticSliderDemo.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="space-y-8">
|
||||
<div class="demo-section">
|
||||
<div class="demo-container relative min-h-[200px] flex items-center justify-center">
|
||||
<ElasticSlider />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2 class="demo-title-extra">Using Steps</h2>
|
||||
<div class="demo-container relative min-h-[200px] flex items-center justify-center">
|
||||
<ElasticSlider :is-stepped="true" :step-size="10" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2 class="demo-title-extra">Custom Range & Icons</h2>
|
||||
<div class="demo-container relative min-h-[200px] flex items-center justify-center">
|
||||
<ElasticSlider :starting-value="500" :default-value="750" :max-value="1000">
|
||||
<template #left-icon>
|
||||
<i class="pi pi-thumbs-down text-xl"></i>
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<i class="pi pi-thumbs-up text-xl"></i>
|
||||
</template>
|
||||
</ElasticSlider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="elasticSlider" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation v-bind="elasticSlider" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TabbedLayout from '@/components/common/TabbedLayout.vue'
|
||||
import PropTable from '@/components/common/PropTable.vue'
|
||||
import CodeExample from '@/components/code/CodeExample.vue'
|
||||
import CliInstallation from '@/components/code/CliInstallation.vue'
|
||||
import ElasticSlider from '@/content/Components/ElasticSlider/ElasticSlider.vue'
|
||||
import { elasticSlider } from '@/constants/code/Components/elasticSliderCode'
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'defaultValue',
|
||||
type: 'number',
|
||||
default: '50',
|
||||
description: 'The initial value of the slider. It can be less than startingValue or greater than maxValue.'
|
||||
},
|
||||
{
|
||||
name: 'startingValue',
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'The starting point for the slider\'s range, e.g., startingValue=100 allows the slider to start at 100.'
|
||||
},
|
||||
{
|
||||
name: 'maxValue',
|
||||
type: 'number',
|
||||
default: '100',
|
||||
description: 'The maximum value the slider can reach.'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: "''",
|
||||
description: 'Allows passing custom class names to style the component.'
|
||||
},
|
||||
{
|
||||
name: 'isStepped',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Enables or disables stepped increments on the slider.'
|
||||
},
|
||||
{
|
||||
name: 'stepSize',
|
||||
type: 'number',
|
||||
default: '1',
|
||||
description: 'The size of the increments for the slider when isStepped is enabled.'
|
||||
},
|
||||
{
|
||||
name: 'leftIcon',
|
||||
type: 'Component | string',
|
||||
default: '\'-\'',
|
||||
description: 'Custom Vue component or string to display on the left side of the slider.'
|
||||
},
|
||||
{
|
||||
name: 'rightIcon',
|
||||
type: 'Component | string',
|
||||
default: '\'+\'',
|
||||
description: 'Custom Vue component or string to display on the right side of the slider.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-container {
|
||||
height: 300px;
|
||||
min-height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.demo-title-extra {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
48
src/demo/Components/FlowingMenuDemo.vue
Normal file
48
src/demo/Components/FlowingMenuDemo.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="flowing-menu-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="height: 600px; overflow: hidden; padding: 100px 0;">
|
||||
<FlowingMenu :items="demoItems" />
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="flowingMenu" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="flowingMenu.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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 FlowingMenu from '../../content/Components/FlowingMenu/FlowingMenu.vue'
|
||||
import { flowingMenu } from '@/constants/code/Components/flowingMenuCode'
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'items',
|
||||
type: 'object[]',
|
||||
default: '[]',
|
||||
description: 'An array of objects containing: link, text, image.'
|
||||
}
|
||||
]
|
||||
|
||||
const demoItems = [
|
||||
{ link: '#', text: 'Mojave', image: 'https://picsum.photos/600/400?random=1' },
|
||||
{ link: '#', text: 'Sonoma', image: 'https://picsum.photos/600/400?random=2' },
|
||||
{ link: '#', text: 'Monterey', image: 'https://picsum.photos/600/400?random=3' },
|
||||
{ link: '#', text: 'Sequoia', image: 'https://picsum.photos/600/400?random=4' }
|
||||
]
|
||||
</script>
|
||||
149
src/demo/Components/FlyingPostersDemo.vue
Normal file
149
src/demo/Components/FlyingPostersDemo.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="flying-posters-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative h-[600px] overflow-hidden flex items-center justify-center">
|
||||
<FlyingPosters :key="rerenderKey" :items="items" :plane-width="planeWidth" :plane-height="planeHeight"
|
||||
:distortion="distortion" :scroll-ease="scrollEase" :camera-fov="cameraFov" :camera-z="cameraZ"
|
||||
class="h-full w-full" />
|
||||
|
||||
<div
|
||||
class="absolute inset-0 flex items-center justify-center pointer-events-none text-[4rem] font-[900] text-[#222] select-none">
|
||||
Scroll.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider title="Plane Width" v-model="planeWidth" :min="200" :max="400" :step="20"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Plane Height" v-model="planeHeight" :min="200" :max="400" :step="20"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Distortion" v-model="distortion" :min="0" :max="10" :step="0.5"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Scroll Ease" v-model="scrollEase" :min="0.005" :max="0.05" :step="0.005"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Camera FOV" v-model="cameraFov" :min="30" :max="90" :step="5"
|
||||
@update:model-value="forceRerender" />
|
||||
|
||||
<PreviewSlider title="Camera Z" v-model="cameraZ" :min="10" :max="40" :step="2"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['ogl']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="flyingPosters" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="flyingPosters.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 FlyingPosters from '../../content/Components/FlyingPosters/FlyingPosters.vue'
|
||||
import { flyingPosters } from '@/constants/code/Components/flyingPostersCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const planeWidth = ref(320)
|
||||
const planeHeight = ref(320)
|
||||
const distortion = ref(3)
|
||||
const scrollEase = ref(0.01)
|
||||
const cameraFov = ref(45)
|
||||
const cameraZ = ref(20)
|
||||
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const items = ref([
|
||||
'https://picsum.photos/800/800?grayscale&random=1',
|
||||
'https://picsum.photos/800/800?grayscale&random=2',
|
||||
'https://picsum.photos/800/800?grayscale&random=3',
|
||||
'https://picsum.photos/800/800?grayscale&random=4',
|
||||
'https://picsum.photos/800/800?grayscale&random=5',
|
||||
'https://picsum.photos/800/800?grayscale&random=6',
|
||||
'https://picsum.photos/800/800?grayscale&random=7',
|
||||
'https://picsum.photos/800/800?grayscale&random=8',
|
||||
'https://picsum.photos/800/800?grayscale&random=9',
|
||||
'https://picsum.photos/800/800?grayscale&random=10',
|
||||
'https://picsum.photos/800/800?grayscale&random=11',
|
||||
'https://picsum.photos/800/800?grayscale&random=12',
|
||||
'https://picsum.photos/800/800?grayscale&random=13',
|
||||
'https://picsum.photos/800/800?grayscale&random=14',
|
||||
'https://picsum.photos/800/800?grayscale&random=15',
|
||||
])
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "items",
|
||||
type: "string[]",
|
||||
default: "[]",
|
||||
description:
|
||||
"An array of image URLs to display in the 3D poster gallery. Each URL should point to a valid image resource."
|
||||
},
|
||||
{
|
||||
name: "planeWidth",
|
||||
type: "number",
|
||||
default: "320",
|
||||
description:
|
||||
"The width of each poster plane in pixels. This affects the visual size of the posters in the 3D space."
|
||||
},
|
||||
{
|
||||
name: "planeHeight",
|
||||
type: "number",
|
||||
default: "320",
|
||||
description:
|
||||
"The height of each poster plane in pixels. This affects the visual size of the posters in the 3D space."
|
||||
},
|
||||
{
|
||||
name: "distortion",
|
||||
type: "number",
|
||||
default: "3",
|
||||
description:
|
||||
"The amount of distortion effect applied to the posters during animation. Higher values create more dramatic effects."
|
||||
},
|
||||
{
|
||||
name: "scrollEase",
|
||||
type: "number",
|
||||
default: "0.01",
|
||||
description:
|
||||
"The easing factor for scroll animation. Lower values create smoother, more fluid scroll transitions."
|
||||
},
|
||||
{
|
||||
name: "cameraFov",
|
||||
type: "number",
|
||||
default: "45",
|
||||
description:
|
||||
"The field of view for the 3D camera in degrees. A wider FOV shows more of the scene but with perspective distortion."
|
||||
},
|
||||
{
|
||||
name: "cameraZ",
|
||||
type: "number",
|
||||
default: "20",
|
||||
description:
|
||||
"The Z-axis position of the 3D camera. Higher values move the camera further back, showing more posters at once."
|
||||
},
|
||||
{
|
||||
name: "className",
|
||||
type: "string",
|
||||
default: "''",
|
||||
description:
|
||||
"Additional CSS classes to apply to the component container."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
63
src/demo/Components/GlassIconsDemo.vue
Normal file
63
src/demo/Components/GlassIconsDemo.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="glass-icons-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="height: 500px; overflow: hidden;">
|
||||
<GlassIcons :items="items" class="my-glass-icons" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Colorful" v-model="colorful" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="glassIcons" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="glassIcons.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 PreviewSwitch from '../../components/common/PreviewSwitch.vue'
|
||||
import GlassIcons from '../../content/Components/GlassIcons/GlassIcons.vue'
|
||||
import { glassIcons } from '@/constants/code/Components/glassIconsCode'
|
||||
|
||||
const colorful = ref(false)
|
||||
|
||||
const items = computed(() => [
|
||||
{ icon: 'pi pi-file', color: colorful.value ? 'blue' : '#444', label: 'Files' },
|
||||
{ icon: 'pi pi-book', color: colorful.value ? 'purple' : '#444', label: 'Books' },
|
||||
{ icon: 'pi pi-heart', color: colorful.value ? 'red' : '#444', label: 'Health' },
|
||||
{ icon: 'pi pi-cloud', color: colorful.value ? 'indigo' : '#444', label: 'Weather' },
|
||||
{ icon: 'pi pi-pencil', color: colorful.value ? 'orange' : '#444', label: 'Notes' },
|
||||
{ icon: 'pi pi-chart-bar', color: colorful.value ? 'green' : '#444', label: 'Stats' }
|
||||
])
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'items',
|
||||
type: 'GlassIconsItem[]',
|
||||
default: '[]',
|
||||
description: 'Array of items to render. Each item should include: an icon (string), a color (string), a label (string), and an optional customClass (string).'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: "''",
|
||||
description: 'Optional additional CSS class(es) to be added to the container.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
145
src/demo/Components/GooeyNavDemo.vue
Normal file
145
src/demo/Components/GooeyNavDemo.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="gooey-nav-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative h-[500px] overflow-hidden bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 flex items-center justify-center">
|
||||
<GooeyNav
|
||||
:key="rerenderKey"
|
||||
:items="navItems"
|
||||
:animation-time="500"
|
||||
:particle-count="particleCount"
|
||||
:particle-distances="[90, 0]"
|
||||
:particle-r="particleR"
|
||||
:time-variance="timeVariance"
|
||||
:initial-active-index="0"
|
||||
:colors="[1, 2, 3, 1, 2, 3, 1, 4]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSlider
|
||||
title="Particle Count"
|
||||
v-model="particleCount"
|
||||
:min="1"
|
||||
:max="50"
|
||||
:step="1"
|
||||
@update:model-value="forceRerender"
|
||||
/>
|
||||
|
||||
<PreviewSlider
|
||||
title="Animation Variance"
|
||||
v-model="timeVariance"
|
||||
:min="0"
|
||||
:max="2000"
|
||||
:step="100"
|
||||
@update:model-value="forceRerender"
|
||||
/>
|
||||
|
||||
<PreviewSlider
|
||||
title="Radius Factor"
|
||||
v-model="particleR"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
:step="100"
|
||||
@update:model-value="forceRerender"
|
||||
/>
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="gooeyNav" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="gooeyNav.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 GooeyNav from '../../content/Components/GooeyNav/GooeyNav.vue'
|
||||
import { gooeyNav } from '@/constants/code/Components/gooeyNavCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const particleCount = ref(15)
|
||||
const timeVariance = ref(300)
|
||||
const particleR = ref(100)
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const navItems = [
|
||||
{ label: "Home", href: null },
|
||||
{ label: "About", href: null },
|
||||
{ label: "Contact", href: null },
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "items",
|
||||
type: "GooeyNavItem[]",
|
||||
default: "[]",
|
||||
description: "Array of navigation items with label and href properties."
|
||||
},
|
||||
{
|
||||
name: "animationTime",
|
||||
type: "number",
|
||||
default: "600",
|
||||
description: "Duration (ms) of the main animation."
|
||||
},
|
||||
{
|
||||
name: "particleCount",
|
||||
type: "number",
|
||||
default: "15",
|
||||
description: "Number of bubble particles per transition."
|
||||
},
|
||||
{
|
||||
name: "particleDistances",
|
||||
type: "[number, number]",
|
||||
default: "[90, 10]",
|
||||
description: "Outer and inner distances of bubble spread."
|
||||
},
|
||||
{
|
||||
name: "particleR",
|
||||
type: "number",
|
||||
default: "100",
|
||||
description: "Radius factor influencing random particle rotation."
|
||||
},
|
||||
{
|
||||
name: "timeVariance",
|
||||
type: "number",
|
||||
default: "300",
|
||||
description: "Random time variance (ms) for particle animations."
|
||||
},
|
||||
{
|
||||
name: "colors",
|
||||
type: "number[]",
|
||||
default: "[1, 2, 3, 1, 2, 3, 1, 4]",
|
||||
description: "Color indices used when creating bubble particles."
|
||||
},
|
||||
{
|
||||
name: "initialActiveIndex",
|
||||
type: "number",
|
||||
default: "0",
|
||||
description: "Which item is selected on mount."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Add color variables for the particles */
|
||||
:root {
|
||||
--color-1: #ff6b6b;
|
||||
--color-2: #4ecdc4;
|
||||
--color-3: #45b7d1;
|
||||
--color-4: #f9ca24;
|
||||
}
|
||||
</style>
|
||||
155
src/demo/Components/InfiniteScrollDemo.vue
Normal file
155
src/demo/Components/InfiniteScrollDemo.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="infinite-scroll-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container"
|
||||
style="height: 500px; overflow: hidden; display: flex; justify-content: center; align-items: center;">
|
||||
<InfiniteScroll :items="items" :is-tilted="isTilted" :tilt-direction="tiltDirection" :autoplay="autoplay"
|
||||
:autoplay-speed="1" :autoplay-direction="autoplayDirection" :pause-on-hover="pauseOnHover" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSwitch title="Tilt" v-model="isTilted" />
|
||||
|
||||
<PreviewSelect v-if="isTilted" title="Tilt Direction" :options="tiltOptions" v-model="tiltDirection" />
|
||||
|
||||
<hr style="margin: 1rem 0; border-color: #222;" />
|
||||
|
||||
<PreviewSwitch title="Autoplay" v-model="autoplay" />
|
||||
|
||||
<template v-if="autoplay">
|
||||
<PreviewSelect title="Autoplay Direction" :options="autoplayOptions" v-model="autoplayDirection" />
|
||||
|
||||
<PreviewSwitch title="Pause on Hover" v-model="pauseOnHover" />
|
||||
</template>
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
<Dependencies :dependency-list="['gsap']" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="infiniteScroll" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="infiniteScroll.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 PreviewSelect from '../../components/common/PreviewSelect.vue'
|
||||
import InfiniteScroll from '../../content/Components/InfiniteScroll/InfiniteScroll.vue'
|
||||
import { infiniteScroll } from '@/constants/code/Components/infiniteScrollCode'
|
||||
|
||||
const isTilted = ref(true)
|
||||
const tiltDirection = ref<'left' | 'right'>('left')
|
||||
const autoplay = ref(true)
|
||||
const autoplayDirection = ref<'up' | 'down'>('up')
|
||||
const pauseOnHover = ref(true)
|
||||
|
||||
const items = [
|
||||
{ content: 'Paragraph Item 2' },
|
||||
{ content: 'Text Item 3' },
|
||||
{ content: 'Paragraph Item 4' },
|
||||
{ content: 'Text Item 5' },
|
||||
{ content: 'Paragraph Item 6' },
|
||||
{ content: 'Text Item 7' },
|
||||
{ content: 'Paragraph Item 8' },
|
||||
{ content: 'Text Item 9' },
|
||||
{ content: 'Paragraph Item 10' },
|
||||
{ content: 'Text Item 11' },
|
||||
{ content: 'Paragraph Item 12' },
|
||||
{ content: 'Text Item 13' },
|
||||
{ content: 'Paragraph Item 14' }
|
||||
]
|
||||
|
||||
const autoplayOptions = [
|
||||
{ value: 'up', label: 'Up' },
|
||||
{ value: 'down', label: 'Down' }
|
||||
]
|
||||
|
||||
const tiltOptions = [
|
||||
{ value: 'left', label: 'Left' },
|
||||
{ value: 'right', label: 'Right' }
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'width',
|
||||
type: 'string',
|
||||
default: '"30rem"',
|
||||
description: 'Width of the outer wrapper.'
|
||||
},
|
||||
{
|
||||
name: 'maxHeight',
|
||||
type: 'string',
|
||||
default: '"100%"',
|
||||
description: 'Maximum height of the outer wrapper.'
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
default: '[]',
|
||||
description: 'Array of items with custom content. Each item should have a "content" property containing a string or Vue component.'
|
||||
},
|
||||
{
|
||||
name: 'itemMinHeight',
|
||||
type: 'number',
|
||||
default: '150',
|
||||
description: 'Fixed height for each item in pixels.'
|
||||
},
|
||||
{
|
||||
name: 'isTilted',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Whether the container has a skewed perspective.'
|
||||
},
|
||||
{
|
||||
name: 'tiltDirection',
|
||||
type: '"left" | "right"',
|
||||
default: '"left"',
|
||||
description: 'Direction of the tilt if "isTilted" is true.'
|
||||
},
|
||||
{
|
||||
name: 'autoplay',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Whether the scroll should autoplay.'
|
||||
},
|
||||
{
|
||||
name: 'autoplaySpeed',
|
||||
type: 'number',
|
||||
default: '0.5',
|
||||
description: 'Speed of autoplay in pixels/frame.'
|
||||
},
|
||||
{
|
||||
name: 'autoplayDirection',
|
||||
type: '"up" | "down"',
|
||||
default: '"down"',
|
||||
description: 'Direction of autoplay scrolling.'
|
||||
},
|
||||
{
|
||||
name: 'pauseOnHover',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Pause autoplay when hovering over the component.'
|
||||
},
|
||||
{
|
||||
name: 'negativeMargin',
|
||||
type: 'string',
|
||||
default: '"-0.5em"',
|
||||
description: 'Negative margin to reduce spacing between items.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
96
src/demo/Components/PixelCardDemo.vue
Normal file
96
src/demo/Components/PixelCardDemo.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="pixel-card-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div
|
||||
class="demo-container relative min-h-[500px] max-h-[500px] overflow-hidden flex items-center justify-center">
|
||||
<PixelCard :key="rerenderKey" :variant="selectedVariant">
|
||||
<div class="absolute mix-blend-screen z-10 inset-0 flex items-center justify-center w-full h-full">
|
||||
<h2 class="text-5xl font-black select-none text-[#222]">
|
||||
Hover Me.
|
||||
</h2>
|
||||
</div>
|
||||
</PixelCard>
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSelect title="Animation Variant" :options="variantOptions" v-model="selectedVariant"
|
||||
@update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="pixelCard" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="pixelCard.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 PreviewSelect from '../../components/common/PreviewSelect.vue'
|
||||
import PixelCard from '../../content/Components/PixelCard/PixelCard.vue'
|
||||
import { pixelCard } from '@/constants/code/Components/pixelCardCode'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const selectedVariant = ref<'default' | 'blue' | 'yellow' | 'pink'>('default')
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const variantOptions = [
|
||||
{ value: 'default', label: 'Default' },
|
||||
{ value: 'yellow', label: 'Yellow' },
|
||||
{ value: 'blue', label: 'Blue' },
|
||||
{ value: 'pink', label: 'Pink' }
|
||||
]
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: "variant",
|
||||
type: "string",
|
||||
default: '"default"',
|
||||
description: "Defines the color scheme and animation style.",
|
||||
options: "default | yellow | blue | pink"
|
||||
},
|
||||
{
|
||||
name: "gap",
|
||||
type: "number",
|
||||
default: "varies by variant",
|
||||
description: "Pixel grid gap size in pixels."
|
||||
},
|
||||
{
|
||||
name: "speed",
|
||||
type: "number",
|
||||
default: "varies by variant",
|
||||
description: "Animation speed modifier (lower is slower)."
|
||||
},
|
||||
{
|
||||
name: "colors",
|
||||
type: "string",
|
||||
default: '"#f8fafc,#f1f5f9,#cbd5e1"',
|
||||
description: "Comma-separated list of colors for the pixel effect."
|
||||
},
|
||||
{
|
||||
name: "noFocus",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description: "If true, prevents animation from triggering on focus."
|
||||
},
|
||||
{
|
||||
name: "className",
|
||||
type: "string",
|
||||
default: '""',
|
||||
description: "Additional CSS class for the wrapper."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
207
src/demo/Components/ProfileCardDemo.vue
Normal file
207
src/demo/Components/ProfileCardDemo.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="profile-card-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container" style="height: 700px; overflow: hidden; position: relative;">
|
||||
<ProfileCard :key="rerenderKey" name="Javi A. Torres" title="Software Engineer" handle="javicodes"
|
||||
status="Online" contact-text="Contact Me" avatar-url="/assets/person.png"
|
||||
:icon-url="showIcon ? '/assets/iconpattern.png' : ''" :show-user-info="showUserInfo"
|
||||
:show-behind-gradient="showBehindGradient" grain-url="/assets/grain.webp"
|
||||
:behind-gradient="customBehindGradient" :inner-gradient="customInnerGradient"
|
||||
@contact-click="handleContactClick" />
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<button @click="generateRandomGradients" class="randomize-btn">
|
||||
Randomize Colors
|
||||
</button>
|
||||
|
||||
<PreviewSwitch title="Show Icon Pattern" v-model="showIcon" @update:model-value="forceRerender" />
|
||||
<PreviewSwitch title="Show User Info" v-model="showUserInfo" @update:model-value="forceRerender" />
|
||||
<PreviewSwitch title="Show BG Gradient" v-model="showBehindGradient" @update:model-value="forceRerender" />
|
||||
</Customize>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="profileCard" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="profileCard.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 ProfileCard from '../../content/Components/ProfileCard/ProfileCard.vue'
|
||||
import { profileCard } from '@/constants/code/Components/profileCardCode'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { useForceRerender } from '@/composables/useForceRerender'
|
||||
|
||||
const showIcon = ref(true)
|
||||
const showUserInfo = ref(true)
|
||||
const showBehindGradient = ref(true)
|
||||
const customBehindGradient = ref("radial-gradient(farthest-side circle at var(--pointer-x) var(--pointer-y),hsla(266,100%,90%,var(--card-opacity)) 4%,hsla(266,50%,80%,calc(var(--card-opacity)*0.75)) 10%,hsla(266,25%,70%,calc(var(--card-opacity)*0.5)) 50%,hsla(266,0%,60%,0) 100%),radial-gradient(35% 52% at 55% 20%,#00ffaac4 0%,#073aff00 100%),radial-gradient(100% 100% at 50% 50%,#00c1ffff 1%,#073aff00 76%),conic-gradient(from 124deg at 50% 50%,#c137ffff 0%,#07c6ffff 40%,#07c6ffff 60%,#c137ffff 100%)")
|
||||
const customInnerGradient = ref("linear-gradient(145deg,#60496e8c 0%,#71C4FF44 100%)")
|
||||
|
||||
const toast = useToast()
|
||||
const { rerenderKey, forceRerender } = useForceRerender()
|
||||
|
||||
const generateRandomGradients = () => {
|
||||
const randomHue1 = Math.floor(Math.random() * 360)
|
||||
const randomHue2 = Math.floor(Math.random() * 360)
|
||||
const randomHue3 = Math.floor(Math.random() * 360)
|
||||
const randomHue4 = Math.floor(Math.random() * 360)
|
||||
|
||||
const newBehindGradient = `radial-gradient(farthest-side circle at var(--pointer-x) var(--pointer-y),hsla(${randomHue1},100%,90%,var(--card-opacity)) 4%,hsla(${randomHue1},50%,80%,calc(var(--card-opacity)*0.75)) 10%,hsla(${randomHue1},25%,70%,calc(var(--card-opacity)*0.5)) 50%,hsla(${randomHue1},0%,60%,0) 100%),radial-gradient(35% 52% at 55% 20%,hsl(${randomHue2}, 100%, 70%) 0%,transparent 100%),radial-gradient(100% 100% at 50% 50%,hsl(${randomHue3}, 100%, 65%) 1%,transparent 76%),conic-gradient(from 124deg at 50% 50%,hsl(${randomHue4}, 100%, 70%) 0%,hsl(${randomHue2}, 100%, 70%) 40%,hsl(${randomHue2}, 100%, 70%) 60%,hsl(${randomHue4}, 100%, 70%) 100%)`
|
||||
const newInnerGradient = `linear-gradient(145deg,hsla(${randomHue1}, 40%, 45%, 0.55) 0%,hsla(${randomHue3}, 60%, 70%, 0.27) 100%)`
|
||||
|
||||
customBehindGradient.value = newBehindGradient
|
||||
customInnerGradient.value = newInnerGradient
|
||||
forceRerender()
|
||||
}
|
||||
|
||||
const handleContactClick = () => {
|
||||
toast.add({
|
||||
severity: 'info',
|
||||
summary: 'Contact Clicked!',
|
||||
detail: 'Contact button was clicked',
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'avatarUrl',
|
||||
type: 'string',
|
||||
default: '"<Placeholder for avatar URL>"',
|
||||
description: 'URL for the main avatar image displayed on the card'
|
||||
},
|
||||
{
|
||||
name: 'iconUrl',
|
||||
type: 'string',
|
||||
default: '"<Placeholder for icon URL>"',
|
||||
description: 'Optional URL for an icon pattern overlay on the card background'
|
||||
},
|
||||
{
|
||||
name: 'grainUrl',
|
||||
type: 'string',
|
||||
default: '"<Placeholder for grain URL>"',
|
||||
description: 'Optional URL for a grain texture overlay effect'
|
||||
},
|
||||
{
|
||||
name: 'behindGradient',
|
||||
type: 'string',
|
||||
default: 'undefined',
|
||||
description: 'Custom CSS gradient string for the background gradient effect'
|
||||
},
|
||||
{
|
||||
name: 'innerGradient',
|
||||
type: 'string',
|
||||
default: 'undefined',
|
||||
description: 'Custom CSS gradient string for the inner card gradient'
|
||||
},
|
||||
{
|
||||
name: 'showBehindGradient',
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description: 'Whether to display the background gradient effect'
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '""',
|
||||
description: 'Additional CSS classes to apply to the card wrapper'
|
||||
},
|
||||
{
|
||||
name: 'enableTilt',
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description: 'Enable or disable the 3D tilt effect on mouse hover'
|
||||
},
|
||||
{
|
||||
name: 'miniAvatarUrl',
|
||||
type: 'string',
|
||||
default: 'undefined',
|
||||
description: 'Optional URL for a smaller avatar in the user info section'
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '"Javi A. Torres"',
|
||||
description: 'User\'s display name'
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '"Software Engineer"',
|
||||
description: 'User\'s job title or role'
|
||||
},
|
||||
{
|
||||
name: 'handle',
|
||||
type: 'string',
|
||||
default: '"javicodes"',
|
||||
description: 'User\'s handle or username (displayed with @ prefix)'
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'string',
|
||||
default: '"Online"',
|
||||
description: 'User\'s current status'
|
||||
},
|
||||
{
|
||||
name: 'contactText',
|
||||
type: 'string',
|
||||
default: '"Contact"',
|
||||
description: 'Text displayed on the contact button'
|
||||
},
|
||||
{
|
||||
name: 'showUserInfo',
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description: 'Whether to display the user information section'
|
||||
},
|
||||
{
|
||||
name: 'onContactClick',
|
||||
type: 'function',
|
||||
default: 'undefined',
|
||||
description: 'Callback function called when the contact button is clicked'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.randomize-btn {
|
||||
font-size: 12px;
|
||||
background: #111;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #333;
|
||||
color: #fff;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.randomize-btn:hover {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
77
src/demo/Components/SpotlightCardDemo.vue
Normal file
77
src/demo/Components/SpotlightCardDemo.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="spotlight-card-demo">
|
||||
<TabbedLayout>
|
||||
<template #preview>
|
||||
<div class="demo-container relative py-10">
|
||||
<SpotlightCard class-name="custom-spotlight-card">
|
||||
<div class="flex h-full flex-col items-start justify-center">
|
||||
<i class="pi pi-star-fill text-4xl mb-3 text-white"></i>
|
||||
<h3 class="text-2xl font-semibold tracking-tight mb-1 text-white">
|
||||
Boost Your Experience
|
||||
</h3>
|
||||
<p class="text-sm text-zinc-400">
|
||||
Get exclusive benefits, features & 24/7 support as a permanent club member.
|
||||
</p>
|
||||
</div>
|
||||
</SpotlightCard>
|
||||
</div>
|
||||
|
||||
<h2 class="text-xl font-semibold text-white mb-4 mt-8">Custom Color</h2>
|
||||
<div class="demo-container relative py-10">
|
||||
<SpotlightCard class-name="custom-spotlight-card" spotlight-color="rgba(39, 255, 100, 0.326)">
|
||||
<div class="flex h-full flex-col items-start justify-center">
|
||||
<i class="pi pi-lock text-3xl mb-3 text-white"></i>
|
||||
<h3 class="text-2xl font-semibold tracking-tight mb-1 text-white">
|
||||
Enhanced Security
|
||||
</h3>
|
||||
<p class="text-sm text-zinc-400">
|
||||
Our state of the art software offers peace of mind through strict security measures.
|
||||
</p>
|
||||
</div>
|
||||
</SpotlightCard>
|
||||
</div>
|
||||
|
||||
<PropTable :data="propData" />
|
||||
</template>
|
||||
|
||||
<template #code>
|
||||
<CodeExample :code-object="spotlightCard" />
|
||||
</template>
|
||||
|
||||
<template #cli>
|
||||
<CliInstallation :command="spotlightCard.cli" />
|
||||
</template>
|
||||
</TabbedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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 SpotlightCard from '../../content/Components/SpotlightCard/SpotlightCard.vue'
|
||||
import { spotlightCard } from '@/constants/code/Components/spotlightCardCode'
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'spotlightColor',
|
||||
type: 'string',
|
||||
default: 'rgba(255, 255, 255, 0.25)',
|
||||
description: 'Controls the color of the radial gradient used for the spotlight effect.',
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Allows adding custom classes to the component.',
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.custom-spotlight-card {
|
||||
min-height: 200px;
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
113
src/demo/TextAnimations/BlurTextDemo.vue
Normal file
113
src/demo/TextAnimations/BlurTextDemo.vue
Normal 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]"> {{ 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]"> {{ 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>
|
||||
75
src/demo/TextAnimations/CircularTextDemo.vue
Normal file
75
src/demo/TextAnimations/CircularTextDemo.vue
Normal 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]"> {{ 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>
|
||||
100
src/demo/TextAnimations/CurvedLoopDemo.vue
Normal file
100
src/demo/TextAnimations/CurvedLoopDemo.vue
Normal 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>
|
||||
160
src/demo/TextAnimations/DecryptedTextDemo.vue
Normal file
160
src/demo/TextAnimations/DecryptedTextDemo.vue
Normal 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>
|
||||
123
src/demo/TextAnimations/FallingTextDemo.vue
Normal file
123
src/demo/TextAnimations/FallingTextDemo.vue
Normal 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>
|
||||
109
src/demo/TextAnimations/FuzzyTextDemo.vue
Normal file
109
src/demo/TextAnimations/FuzzyTextDemo.vue
Normal 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>
|
||||
121
src/demo/TextAnimations/GradientTextDemo.vue
Normal file
121
src/demo/TextAnimations/GradientTextDemo.vue
Normal 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>
|
||||
97
src/demo/TextAnimations/ShinyTextDemo.vue
Normal file
97
src/demo/TextAnimations/ShinyTextDemo.vue
Normal 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>
|
||||
111
src/demo/TextAnimations/TextCursorDemo.vue
Normal file
111
src/demo/TextAnimations/TextCursorDemo.vue
Normal 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>
|
||||
176
src/demo/TextAnimations/TextPressureDemo.vue
Normal file
176
src/demo/TextAnimations/TextPressureDemo.vue
Normal 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>
|
||||
150
src/demo/TextAnimations/TextTrailDemo.vue
Normal file
150
src/demo/TextAnimations/TextTrailDemo.vue
Normal 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>
|
||||
Reference in New Issue
Block a user