mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07: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>
|
||||
Reference in New Issue
Block a user