mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 06:29:30 -07:00
Merge branch 'main' into refact/metallic-paint
This commit is contained in:
@@ -41,7 +41,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, defineComponent, h, useTemplateRef } from 'vue';
|
||||
import { gsap } from 'gsap';
|
||||
import CountUp from '../../../content/Animations/CountUp/CountUp.vue';
|
||||
import CountUp from '../../../content/TextAnimations/CountUp/CountUp.vue';
|
||||
import './FeatureCards.css';
|
||||
|
||||
const isMobile = ref(false);
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
// Highlighted sidebar items
|
||||
export const NEW = ['Antigravity', 'Color Bends', 'Ghost Cursor', 'Laser Flow', 'Liquid Ether', 'Pixel Blast', 'Floating Lines', 'Light Pillar', 'Pixel Snow', 'Grid Scan'];
|
||||
export const NEW = [
|
||||
'Antigravity',
|
||||
'Color Bends',
|
||||
'Floating Lines',
|
||||
'Ghost Cursor',
|
||||
'Grid Scan',
|
||||
'Laser Flow',
|
||||
'Light Pillar',
|
||||
'Liquid Ether',
|
||||
'Pixel Blast',
|
||||
'Pixel Snow',
|
||||
];
|
||||
export const UPDATED = ['Metallic Paint'];
|
||||
|
||||
// Used for main sidebar navigation
|
||||
export const CATEGORIES = [
|
||||
{
|
||||
name: 'Get Started',
|
||||
subcategories: ['Index']
|
||||
subcategories: [ 'Index' ],
|
||||
},
|
||||
{
|
||||
name: 'Text Animations',
|
||||
@@ -14,6 +25,7 @@ export const CATEGORIES = [
|
||||
'Ascii Text',
|
||||
'Blur Text',
|
||||
'Circular Text',
|
||||
'Count Up',
|
||||
'Curved Loop',
|
||||
'Decrypted Text',
|
||||
'Falling Text',
|
||||
@@ -43,7 +55,6 @@ export const CATEGORIES = [
|
||||
'Antigravity',
|
||||
'Blob Cursor',
|
||||
'Click Spark',
|
||||
'Count Up',
|
||||
'Crosshair',
|
||||
'Cubes',
|
||||
'Electric Border',
|
||||
@@ -56,8 +67,8 @@ export const CATEGORIES = [
|
||||
'Logo Loop',
|
||||
'Magnet',
|
||||
'Magnet Lines',
|
||||
'Meta Balls',
|
||||
'Metallic Paint',
|
||||
'Meta Balls',
|
||||
'Noise',
|
||||
'Pixel Trail',
|
||||
'Pixel Transition',
|
||||
@@ -95,8 +106,8 @@ export const CATEGORIES = [
|
||||
'Infinite Scroll',
|
||||
'Magic Bento',
|
||||
'Masonry',
|
||||
'Pixel Card',
|
||||
'Pill Nav',
|
||||
'Pixel Card',
|
||||
'Profile Card',
|
||||
'Rolling Gallery',
|
||||
'Scroll Stack',
|
||||
@@ -128,9 +139,9 @@ export const CATEGORIES = [
|
||||
'Hyperspeed',
|
||||
'Iridescence',
|
||||
'Letter Glitch',
|
||||
'Lightning',
|
||||
'Light Pillar',
|
||||
'Light Rays',
|
||||
'Lightning',
|
||||
'Liquid Chrome',
|
||||
'Liquid Ether',
|
||||
'Orb',
|
||||
|
||||
@@ -10,7 +10,6 @@ const animations = {
|
||||
'metallic-paint': () => import('../demo/Animations/MetallicPaintDemo.vue'),
|
||||
'magnet': () => import('../demo/Animations/MagnetDemo.vue'),
|
||||
'cubes': () => import('../demo/Animations/CubesDemo.vue'),
|
||||
'count-up': () => import('../demo/Animations/CountUpDemo.vue'),
|
||||
'splash-cursor': () => import('../demo/Animations/SplashCursorDemo.vue'),
|
||||
'noise': () => import('../demo/Animations/NoiseDemo.vue'),
|
||||
'star-border': () => import('../demo/Animations/StarBorderDemo.vue'),
|
||||
@@ -35,6 +34,7 @@ const textAnimations = {
|
||||
'circular-text': () => import("../demo/TextAnimations/CircularTextDemo.vue"),
|
||||
'shiny-text': () => import("../demo/TextAnimations/ShinyTextDemo.vue"),
|
||||
'text-pressure': () => import("../demo/TextAnimations/TextPressureDemo.vue"),
|
||||
'count-up': () => import('../demo/TextAnimations/CountUpDemo.vue'),
|
||||
'curved-loop': () => import("../demo/TextAnimations/CurvedLoopDemo.vue"),
|
||||
'fuzzy-text': () => import("../demo/TextAnimations/FuzzyTextDemo.vue"),
|
||||
'gradient-text': () => import("../demo/TextAnimations/GradientTextDemo.vue"),
|
||||
|
||||
@@ -214,14 +214,6 @@ export const componentMetadata: ComponentMetadata = {
|
||||
docsUrl: 'https://vue-bits.dev/animations/laser-flow',
|
||||
tags: []
|
||||
},
|
||||
'Animations/CountUp': {
|
||||
videoUrl: '/assets/videos/countup.webm',
|
||||
description: 'Animated number counter supporting formatting and decimals.',
|
||||
category: 'Animations',
|
||||
name: 'CountUp',
|
||||
docsUrl: 'https://vue-bits.dev/text-animations/count-up',
|
||||
tags: []
|
||||
},
|
||||
'Animations/PixelTrail': {
|
||||
videoUrl: '/assets/videos/pixeltrail.webm',
|
||||
description: 'Pixel grid trail effect that follows cursor movement with customizable gooey filter.',
|
||||
@@ -241,6 +233,14 @@ export const componentMetadata: ComponentMetadata = {
|
||||
docsUrl: 'https://vue-bits.dev/text-animations/ascii-text',
|
||||
tags: []
|
||||
},
|
||||
'TextAnimations/CountUp': {
|
||||
videoUrl: '/assets/videos/countup.webm',
|
||||
description: 'Animated number counter supporting formatting and decimals.',
|
||||
category: 'TextAnimations',
|
||||
name: 'CountUp',
|
||||
docsUrl: 'https://vue-bits.dev/text-animations/count-up',
|
||||
tags: []
|
||||
},
|
||||
'TextAnimations/BlurText': {
|
||||
videoUrl: '/assets/videos/blurtext.webm',
|
||||
description: 'Text starts blurred then crisply resolves for a soft-focus reveal effect.',
|
||||
|
||||
@@ -21,7 +21,7 @@ export const blurText = createCodeObject(code, 'TextAnimations/BlurText', {
|
||||
import BlurText from "./BlurText.vue";
|
||||
|
||||
const handleAnimationComplete = () => {
|
||||
console.log('All animations complete!');,
|
||||
});
|
||||
console.log('All animations complete!');
|
||||
};
|
||||
</script>`
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import code from '@/content/Animations/CountUp/CountUp.vue?raw';
|
||||
import code from '@/content/TextAnimations/CountUp/CountUp.vue?raw';
|
||||
import { createCodeObject } from '@/types/code';
|
||||
|
||||
export const countup = createCodeObject(code, 'Animations/CountUp', {
|
||||
export const countup = createCodeObject(code, 'TextAnimations/CountUp', {
|
||||
usage: `<template>
|
||||
<CountUp
|
||||
:from="0"
|
||||
@@ -16,7 +16,7 @@ export interface ShuffleProps {
|
||||
text: string;
|
||||
className?: string;
|
||||
style?: Record<string, any>;
|
||||
shuffleDirection?: 'left' | 'right';
|
||||
shuffleDirection?: 'left' | 'right' | 'up' | 'down';
|
||||
duration?: number;
|
||||
maxDelay?: number;
|
||||
ease?: string | ((t: number) => number);
|
||||
@@ -152,54 +152,87 @@ const build = () => {
|
||||
if (!parent) return;
|
||||
|
||||
const w = ch.getBoundingClientRect().width;
|
||||
const h = ch.getBoundingClientRect().height;
|
||||
if (!w) return;
|
||||
|
||||
const wrap = document.createElement('span');
|
||||
wrap.className = 'inline-block overflow-hidden align-baseline text-left';
|
||||
Object.assign(wrap.style, { width: w + 'px' });
|
||||
wrap.className = 'inline-block overflow-hidden text-left';
|
||||
Object.assign(wrap.style, {
|
||||
width: w + 'px',
|
||||
height: props.shuffleDirection === 'up' || props.shuffleDirection === 'down' ? h + 'px' : 'auto',
|
||||
verticalAlign: 'bottom'
|
||||
});
|
||||
|
||||
const inner = document.createElement('span');
|
||||
inner.className = 'inline-block whitespace-nowrap will-change-transform origin-left transform-gpu';
|
||||
inner.className =
|
||||
'inline-block will-change-transform origin-left transform-gpu ' +
|
||||
(props.shuffleDirection === 'up' || props.shuffleDirection === 'down'
|
||||
? 'whitespace-normal'
|
||||
: 'whitespace-nowrap');
|
||||
|
||||
parent.insertBefore(wrap, ch);
|
||||
wrap.appendChild(inner);
|
||||
|
||||
const firstOrig = ch.cloneNode(true) as HTMLElement;
|
||||
firstOrig.className = 'inline-block text-left';
|
||||
firstOrig.className =
|
||||
'text-left ' + (props.shuffleDirection === 'up' || props.shuffleDirection === 'down' ? 'block' : 'inline-block');
|
||||
Object.assign(firstOrig.style, { width: w + 'px', fontFamily: computedFont });
|
||||
|
||||
ch.setAttribute('data-orig', '1');
|
||||
ch.className = 'inline-block text-left';
|
||||
ch.className =
|
||||
'text-left ' + (props.shuffleDirection === 'up' || props.shuffleDirection === 'down' ? 'block' : 'inline-block');
|
||||
Object.assign(ch.style, { width: w + 'px', fontFamily: computedFont });
|
||||
|
||||
inner.appendChild(firstOrig);
|
||||
for (let k = 0; k < rolls; k++) {
|
||||
const c = ch.cloneNode(true) as HTMLElement;
|
||||
if (props.scrambleCharset) c.textContent = rand(props.scrambleCharset);
|
||||
c.className = 'inline-block text-left';
|
||||
c.className =
|
||||
'text-left ' +
|
||||
(props.shuffleDirection === 'up' || props.shuffleDirection === 'down' ? 'block' : 'inline-block');
|
||||
Object.assign(c.style, { width: w + 'px', fontFamily: computedFont });
|
||||
inner.appendChild(c);
|
||||
}
|
||||
inner.appendChild(ch);
|
||||
|
||||
const steps = rolls + 1;
|
||||
let startX = 0;
|
||||
let finalX = -steps * w;
|
||||
if (props.shuffleDirection === 'right') {
|
||||
if (props.shuffleDirection === 'right' || props.shuffleDirection === 'down') {
|
||||
const firstCopy = inner.firstElementChild as HTMLElement | null;
|
||||
const real = inner.lastElementChild as HTMLElement | null;
|
||||
if (real) inner.insertBefore(real, inner.firstChild);
|
||||
if (firstCopy) inner.appendChild(firstCopy);
|
||||
startX = -steps * w;
|
||||
finalX = 0;
|
||||
}
|
||||
|
||||
gsap.set(inner, { x: startX, force3D: true });
|
||||
let startX = 0;
|
||||
let finalX = 0;
|
||||
let startY = 0;
|
||||
let finalY = 0;
|
||||
|
||||
if (props.shuffleDirection === 'right') {
|
||||
startX = -steps * w;
|
||||
finalX = 0;
|
||||
} else if (props.shuffleDirection === 'left') {
|
||||
startX = 0;
|
||||
finalX = -steps * w;
|
||||
} else if (props.shuffleDirection === 'down') {
|
||||
startY = -steps * h;
|
||||
finalY = 0;
|
||||
} else if (props.shuffleDirection === 'up') {
|
||||
startY = 0;
|
||||
finalY = -steps * h;
|
||||
}
|
||||
|
||||
if (props.shuffleDirection === 'left' || props.shuffleDirection === 'right') {
|
||||
gsap.set(inner, { x: startX, y: 0, force3D: true });
|
||||
inner.setAttribute('data-start-x', String(startX));
|
||||
inner.setAttribute('data-final-x', String(finalX));
|
||||
} else {
|
||||
gsap.set(inner, { x: 0, y: startY, force3D: true });
|
||||
inner.setAttribute('data-start-y', String(startY));
|
||||
inner.setAttribute('data-final-y', String(finalY));
|
||||
}
|
||||
|
||||
if (props.colorFrom) (inner.style as any).color = props.colorFrom;
|
||||
|
||||
inner.setAttribute('data-final-x', String(finalX));
|
||||
inner.setAttribute('data-start-x', String(startX));
|
||||
|
||||
wrappersRef.value.push(wrap);
|
||||
});
|
||||
};
|
||||
@@ -248,6 +281,7 @@ const play = () => {
|
||||
if (!strips.length) return;
|
||||
|
||||
playingRef.value = true;
|
||||
const isVertical = props.shuffleDirection === 'up' || props.shuffleDirection === 'down';
|
||||
|
||||
const tl = gsap.timeline({
|
||||
smoothChildTiming: true,
|
||||
@@ -255,7 +289,11 @@ const play = () => {
|
||||
repeatDelay: props.loop ? props.loopDelay : 0,
|
||||
onRepeat: () => {
|
||||
if (props.scrambleCharset) randomizeScrambles();
|
||||
gsap.set(strips, { x: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-start-x') || '0') });
|
||||
if (isVertical) {
|
||||
gsap.set(strips, { y: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-start-y') || '0') });
|
||||
} else {
|
||||
gsap.set(strips, { x: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-start-x') || '0') });
|
||||
}
|
||||
emit('shuffle-complete');
|
||||
props.onShuffleComplete?.();
|
||||
},
|
||||
@@ -272,17 +310,19 @@ const play = () => {
|
||||
});
|
||||
|
||||
const addTween = (targets: HTMLElement[], at: number) => {
|
||||
tl.to(
|
||||
targets,
|
||||
{
|
||||
x: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-final-x') || '0'),
|
||||
duration: props.duration,
|
||||
ease: props.ease,
|
||||
force3D: true,
|
||||
stagger: props.animationMode === 'evenodd' ? props.stagger : 0
|
||||
},
|
||||
at
|
||||
);
|
||||
const vars: any = {
|
||||
duration: props.duration,
|
||||
ease: props.ease,
|
||||
force3D: true,
|
||||
stagger: props.animationMode === 'evenodd' ? props.stagger : 0
|
||||
};
|
||||
if (isVertical) {
|
||||
vars.y = (i: number, t: HTMLElement) => parseFloat(t.getAttribute('data-final-y') || '0');
|
||||
} else {
|
||||
vars.x = (i: number, t: HTMLElement) => parseFloat(t.getAttribute('data-final-x') || '0');
|
||||
}
|
||||
|
||||
tl.to(targets, vars, at);
|
||||
if (props.colorFrom && props.colorTo)
|
||||
tl.to(targets, { color: props.colorTo, duration: props.duration, ease: props.ease }, at);
|
||||
};
|
||||
@@ -297,16 +337,17 @@ const play = () => {
|
||||
} else {
|
||||
strips.forEach(strip => {
|
||||
const d = Math.random() * props.maxDelay;
|
||||
tl.to(
|
||||
strip,
|
||||
{
|
||||
x: parseFloat(strip.getAttribute('data-final-x') || '0'),
|
||||
duration: props.duration,
|
||||
ease: props.ease,
|
||||
force3D: true
|
||||
},
|
||||
d
|
||||
);
|
||||
const vars: any = {
|
||||
duration: props.duration,
|
||||
ease: props.ease,
|
||||
force3D: true
|
||||
};
|
||||
if (isVertical) {
|
||||
vars.y = parseFloat(strip.getAttribute('data-final-y') || '0');
|
||||
} else {
|
||||
vars.x = parseFloat(strip.getAttribute('data-final-x') || '0');
|
||||
}
|
||||
tl.to(strip, vars, d);
|
||||
if (props.colorFrom && props.colorTo)
|
||||
tl.fromTo(
|
||||
strip,
|
||||
|
||||
@@ -71,8 +71,8 @@ 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 CountUp from '../../content/TextAnimations/CountUp/CountUp.vue';
|
||||
import { countup } from '@/constants/code/TextAnimations/countUpCode';
|
||||
import { useForceRerender } from '@/composables/useForceRerender';
|
||||
import Customize from '../../components/common/Customize.vue';
|
||||
import PreviewSlider from '../../components/common/PreviewSlider.vue';
|
||||
@@ -80,7 +80,7 @@ const { rerenderKey: key, forceRerender } = useForceRerender();
|
||||
const duration = ref(0.35);
|
||||
const shuffleTimes = ref(1);
|
||||
const stagger = ref(0.03);
|
||||
const shuffleDirection = ref<'left' | 'right'>('right');
|
||||
const shuffleDirection = ref<'left' | 'right' | 'up' | 'down'>('right');
|
||||
const ease = ref('power3.out');
|
||||
const loop = ref(false);
|
||||
const loopDelay = ref(0);
|
||||
@@ -88,7 +88,9 @@ const triggerOnHover = ref(true);
|
||||
|
||||
const directionOptions = [
|
||||
{ label: 'Right', value: 'right' },
|
||||
{ label: 'Left', value: 'left' }
|
||||
{ label: 'Left', value: 'left' },
|
||||
{ label: 'Up', value: 'up' },
|
||||
{ label: 'Down', value: 'down' }
|
||||
];
|
||||
|
||||
const easeOptions = [
|
||||
@@ -104,7 +106,7 @@ const propData = [
|
||||
{ name: 'style', type: 'object', default: '{}', description: 'Inline styles applied to the wrapper element.' },
|
||||
{
|
||||
name: 'shuffleDirection',
|
||||
type: '"left" | "right"',
|
||||
type: '"left" | "right" | "up" | "down"',
|
||||
default: '"right"',
|
||||
description: 'Direction the per-letter strip slides to reveal the final character.'
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
|
||||
<Customize>
|
||||
<PreviewSelect v-model="cursorCharacter" :options="['|', '_', '█', '▌', '▐']" title="Cursor Character" />
|
||||
<PreviewSelect v-model="cursorCharacter" :options="cursorOptions" title="Cursor Character" />
|
||||
<PreviewSlider v-model="typingSpeed" title="Typing Speed" :min="10" :max="200" :step="5" value-unit="ms" />
|
||||
<PreviewSlider
|
||||
v-model="pauseDuration"
|
||||
@@ -99,6 +99,14 @@ const cursorBlinkDuration = ref(0.5);
|
||||
|
||||
const { rerenderKey: key } = useForceRerender();
|
||||
|
||||
const cursorOptions = [
|
||||
{ value: '_', label: 'Underscore (_)' },
|
||||
{ value: '|', label: 'Pipe (|)' },
|
||||
{ value: '▎', label: 'Block (▎)' },
|
||||
{ value: '●', label: 'Dot (●)' },
|
||||
{ value: '█', label: 'Full Block (█)' }
|
||||
];
|
||||
|
||||
const propData = [
|
||||
{
|
||||
name: 'text',
|
||||
|
||||
Reference in New Issue
Block a user