mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 14:39:30 -07:00
[ ADDED ] : Up & Down Shuffle TextAnimation
This commit is contained in:
@@ -16,7 +16,7 @@ export interface ShuffleProps {
|
|||||||
text: string;
|
text: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: Record<string, any>;
|
style?: Record<string, any>;
|
||||||
shuffleDirection?: 'left' | 'right';
|
shuffleDirection?: 'left' | 'right' | 'up' | 'down';
|
||||||
duration?: number;
|
duration?: number;
|
||||||
maxDelay?: number;
|
maxDelay?: number;
|
||||||
ease?: string | ((t: number) => number);
|
ease?: string | ((t: number) => number);
|
||||||
@@ -152,54 +152,87 @@ const build = () => {
|
|||||||
if (!parent) return;
|
if (!parent) return;
|
||||||
|
|
||||||
const w = ch.getBoundingClientRect().width;
|
const w = ch.getBoundingClientRect().width;
|
||||||
|
const h = ch.getBoundingClientRect().height;
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
||||||
const wrap = document.createElement('span');
|
const wrap = document.createElement('span');
|
||||||
wrap.className = 'inline-block overflow-hidden align-baseline text-left';
|
wrap.className = 'inline-block overflow-hidden text-left';
|
||||||
Object.assign(wrap.style, { width: w + 'px' });
|
Object.assign(wrap.style, {
|
||||||
|
width: w + 'px',
|
||||||
|
height: props.shuffleDirection === 'up' || props.shuffleDirection === 'down' ? h + 'px' : 'auto',
|
||||||
|
verticalAlign: 'bottom'
|
||||||
|
});
|
||||||
|
|
||||||
const inner = document.createElement('span');
|
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);
|
parent.insertBefore(wrap, ch);
|
||||||
wrap.appendChild(inner);
|
wrap.appendChild(inner);
|
||||||
|
|
||||||
const firstOrig = ch.cloneNode(true) as HTMLElement;
|
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 });
|
Object.assign(firstOrig.style, { width: w + 'px', fontFamily: computedFont });
|
||||||
|
|
||||||
ch.setAttribute('data-orig', '1');
|
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 });
|
Object.assign(ch.style, { width: w + 'px', fontFamily: computedFont });
|
||||||
|
|
||||||
inner.appendChild(firstOrig);
|
inner.appendChild(firstOrig);
|
||||||
for (let k = 0; k < rolls; k++) {
|
for (let k = 0; k < rolls; k++) {
|
||||||
const c = ch.cloneNode(true) as HTMLElement;
|
const c = ch.cloneNode(true) as HTMLElement;
|
||||||
if (props.scrambleCharset) c.textContent = rand(props.scrambleCharset);
|
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 });
|
Object.assign(c.style, { width: w + 'px', fontFamily: computedFont });
|
||||||
inner.appendChild(c);
|
inner.appendChild(c);
|
||||||
}
|
}
|
||||||
inner.appendChild(ch);
|
inner.appendChild(ch);
|
||||||
|
|
||||||
const steps = rolls + 1;
|
const steps = rolls + 1;
|
||||||
let startX = 0;
|
if (props.shuffleDirection === 'right' || props.shuffleDirection === 'down') {
|
||||||
let finalX = -steps * w;
|
|
||||||
if (props.shuffleDirection === 'right') {
|
|
||||||
const firstCopy = inner.firstElementChild as HTMLElement | null;
|
const firstCopy = inner.firstElementChild as HTMLElement | null;
|
||||||
const real = inner.lastElementChild as HTMLElement | null;
|
const real = inner.lastElementChild as HTMLElement | null;
|
||||||
if (real) inner.insertBefore(real, inner.firstChild);
|
if (real) inner.insertBefore(real, inner.firstChild);
|
||||||
if (firstCopy) inner.appendChild(firstCopy);
|
if (firstCopy) inner.appendChild(firstCopy);
|
||||||
startX = -steps * w;
|
|
||||||
finalX = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gsap.set(inner, { x: startX, force3D: true });
|
let startX = 0;
|
||||||
if (props.colorFrom) (inner.style as any).color = props.colorFrom;
|
let finalX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let finalY = 0;
|
||||||
|
|
||||||
inner.setAttribute('data-final-x', String(finalX));
|
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-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;
|
||||||
wrappersRef.value.push(wrap);
|
wrappersRef.value.push(wrap);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -248,6 +281,7 @@ const play = () => {
|
|||||||
if (!strips.length) return;
|
if (!strips.length) return;
|
||||||
|
|
||||||
playingRef.value = true;
|
playingRef.value = true;
|
||||||
|
const isVertical = props.shuffleDirection === 'up' || props.shuffleDirection === 'down';
|
||||||
|
|
||||||
const tl = gsap.timeline({
|
const tl = gsap.timeline({
|
||||||
smoothChildTiming: true,
|
smoothChildTiming: true,
|
||||||
@@ -255,7 +289,11 @@ const play = () => {
|
|||||||
repeatDelay: props.loop ? props.loopDelay : 0,
|
repeatDelay: props.loop ? props.loopDelay : 0,
|
||||||
onRepeat: () => {
|
onRepeat: () => {
|
||||||
if (props.scrambleCharset) randomizeScrambles();
|
if (props.scrambleCharset) randomizeScrambles();
|
||||||
|
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') });
|
gsap.set(strips, { x: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-start-x') || '0') });
|
||||||
|
}
|
||||||
emit('shuffle-complete');
|
emit('shuffle-complete');
|
||||||
props.onShuffleComplete?.();
|
props.onShuffleComplete?.();
|
||||||
},
|
},
|
||||||
@@ -272,17 +310,19 @@ const play = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const addTween = (targets: HTMLElement[], at: number) => {
|
const addTween = (targets: HTMLElement[], at: number) => {
|
||||||
tl.to(
|
const vars: any = {
|
||||||
targets,
|
|
||||||
{
|
|
||||||
x: (i, t: HTMLElement) => parseFloat(t.getAttribute('data-final-x') || '0'),
|
|
||||||
duration: props.duration,
|
duration: props.duration,
|
||||||
ease: props.ease,
|
ease: props.ease,
|
||||||
force3D: true,
|
force3D: true,
|
||||||
stagger: props.animationMode === 'evenodd' ? props.stagger : 0
|
stagger: props.animationMode === 'evenodd' ? props.stagger : 0
|
||||||
},
|
};
|
||||||
at
|
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)
|
if (props.colorFrom && props.colorTo)
|
||||||
tl.to(targets, { color: props.colorTo, duration: props.duration, ease: props.ease }, at);
|
tl.to(targets, { color: props.colorTo, duration: props.duration, ease: props.ease }, at);
|
||||||
};
|
};
|
||||||
@@ -297,16 +337,17 @@ const play = () => {
|
|||||||
} else {
|
} else {
|
||||||
strips.forEach(strip => {
|
strips.forEach(strip => {
|
||||||
const d = Math.random() * props.maxDelay;
|
const d = Math.random() * props.maxDelay;
|
||||||
tl.to(
|
const vars: any = {
|
||||||
strip,
|
|
||||||
{
|
|
||||||
x: parseFloat(strip.getAttribute('data-final-x') || '0'),
|
|
||||||
duration: props.duration,
|
duration: props.duration,
|
||||||
ease: props.ease,
|
ease: props.ease,
|
||||||
force3D: true
|
force3D: true
|
||||||
},
|
};
|
||||||
d
|
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)
|
if (props.colorFrom && props.colorTo)
|
||||||
tl.fromTo(
|
tl.fromTo(
|
||||||
strip,
|
strip,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const { rerenderKey: key, forceRerender } = useForceRerender();
|
|||||||
const duration = ref(0.35);
|
const duration = ref(0.35);
|
||||||
const shuffleTimes = ref(1);
|
const shuffleTimes = ref(1);
|
||||||
const stagger = ref(0.03);
|
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 ease = ref('power3.out');
|
||||||
const loop = ref(false);
|
const loop = ref(false);
|
||||||
const loopDelay = ref(0);
|
const loopDelay = ref(0);
|
||||||
@@ -88,7 +88,9 @@ const triggerOnHover = ref(true);
|
|||||||
|
|
||||||
const directionOptions = [
|
const directionOptions = [
|
||||||
{ label: 'Right', value: 'right' },
|
{ label: 'Right', value: 'right' },
|
||||||
{ label: 'Left', value: 'left' }
|
{ label: 'Left', value: 'left' },
|
||||||
|
{ label: 'Up', value: 'up' },
|
||||||
|
{ label: 'Down', value: 'down' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const easeOptions = [
|
const easeOptions = [
|
||||||
@@ -104,7 +106,7 @@ const propData = [
|
|||||||
{ name: 'style', type: 'object', default: '{}', description: 'Inline styles applied to the wrapper element.' },
|
{ name: 'style', type: 'object', default: '{}', description: 'Inline styles applied to the wrapper element.' },
|
||||||
{
|
{
|
||||||
name: 'shuffleDirection',
|
name: 'shuffleDirection',
|
||||||
type: '"left" | "right"',
|
type: '"left" | "right" | "up" | "down"',
|
||||||
default: '"right"',
|
default: '"right"',
|
||||||
description: 'Direction the per-letter strip slides to reveal the final character.'
|
description: 'Direction the per-letter strip slides to reveal the final character.'
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user