mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 14:39:30 -07:00
refactor(text-animations): convert GlitchText from CSS file to Tailwind classes
- Remove external GlitchText.css file dependency - Convert all styles to Tailwind utility classes - Maintain identical functionality and visual effects - Improve component maintainability and consistency
This commit is contained in:
@@ -1,120 +0,0 @@
|
|||||||
.glitch {
|
|
||||||
color: #fff;
|
|
||||||
font-size: clamp(2rem, 10vw, 8rem);
|
|
||||||
white-space: nowrap;
|
|
||||||
font-weight: 900;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glitch::after,
|
|
||||||
.glitch::before {
|
|
||||||
content: attr(data-text);
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #060010;
|
|
||||||
overflow: hidden;
|
|
||||||
clip-path: inset(0 0 0 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.glitch:not(.enable-on-hover)::after {
|
|
||||||
left: 10px;
|
|
||||||
text-shadow: var(--after-shadow, -10px 0 red);
|
|
||||||
animation: animate-glitch var(--after-duration, 3s) infinite linear alternate-reverse;
|
|
||||||
}
|
|
||||||
.glitch:not(.enable-on-hover)::before {
|
|
||||||
left: -10px;
|
|
||||||
text-shadow: var(--before-shadow, 10px 0 cyan);
|
|
||||||
animation: animate-glitch var(--before-duration, 2s) infinite linear alternate-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glitch.enable-on-hover::after,
|
|
||||||
.glitch.enable-on-hover::before {
|
|
||||||
content: '';
|
|
||||||
opacity: 0;
|
|
||||||
animation: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glitch.enable-on-hover:hover::after {
|
|
||||||
content: attr(data-text);
|
|
||||||
opacity: 1;
|
|
||||||
left: 10px;
|
|
||||||
text-shadow: var(--after-shadow, -10px 0 red);
|
|
||||||
animation: animate-glitch var(--after-duration, 3s) infinite linear alternate-reverse;
|
|
||||||
}
|
|
||||||
.glitch.enable-on-hover:hover::before {
|
|
||||||
content: attr(data-text);
|
|
||||||
opacity: 1;
|
|
||||||
left: -10px;
|
|
||||||
text-shadow: var(--before-shadow, 10px 0 cyan);
|
|
||||||
animation: animate-glitch var(--before-duration, 2s) infinite linear alternate-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animate-glitch {
|
|
||||||
0% {
|
|
||||||
clip-path: inset(20% 0 50% 0);
|
|
||||||
}
|
|
||||||
5% {
|
|
||||||
clip-path: inset(10% 0 60% 0);
|
|
||||||
}
|
|
||||||
10% {
|
|
||||||
clip-path: inset(15% 0 55% 0);
|
|
||||||
}
|
|
||||||
15% {
|
|
||||||
clip-path: inset(25% 0 35% 0);
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
clip-path: inset(30% 0 40% 0);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
clip-path: inset(40% 0 20% 0);
|
|
||||||
}
|
|
||||||
30% {
|
|
||||||
clip-path: inset(10% 0 60% 0);
|
|
||||||
}
|
|
||||||
35% {
|
|
||||||
clip-path: inset(15% 0 55% 0);
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
clip-path: inset(25% 0 35% 0);
|
|
||||||
}
|
|
||||||
45% {
|
|
||||||
clip-path: inset(30% 0 40% 0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
clip-path: inset(20% 0 50% 0);
|
|
||||||
}
|
|
||||||
55% {
|
|
||||||
clip-path: inset(10% 0 60% 0);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
clip-path: inset(15% 0 55% 0);
|
|
||||||
}
|
|
||||||
65% {
|
|
||||||
clip-path: inset(25% 0 35% 0);
|
|
||||||
}
|
|
||||||
70% {
|
|
||||||
clip-path: inset(30% 0 40% 0);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
clip-path: inset(40% 0 20% 0);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
clip-path: inset(20% 0 50% 0);
|
|
||||||
}
|
|
||||||
85% {
|
|
||||||
clip-path: inset(10% 0 60% 0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
clip-path: inset(15% 0 55% 0);
|
|
||||||
}
|
|
||||||
95% {
|
|
||||||
clip-path: inset(25% 0 35% 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
clip-path: inset(30% 0 40% 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
import './GlitchText.css';
|
|
||||||
|
|
||||||
interface GlitchTextProps {
|
interface GlitchTextProps {
|
||||||
children: string;
|
children: string;
|
||||||
@@ -40,8 +39,151 @@ const inlineStyles = computed(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const baseClasses = [
|
||||||
|
// Base styling
|
||||||
|
'text-white',
|
||||||
|
'font-black',
|
||||||
|
'whitespace-nowrap',
|
||||||
|
'relative',
|
||||||
|
'mx-auto',
|
||||||
|
'select-none',
|
||||||
|
'cursor-pointer',
|
||||||
|
'text-[clamp(2rem,10vw,8rem)]',
|
||||||
|
|
||||||
|
// Pseudo-elements base
|
||||||
|
'before:content-[attr(data-text)]',
|
||||||
|
'before:absolute',
|
||||||
|
'before:top-0',
|
||||||
|
'before:text-white',
|
||||||
|
'before:bg-[#060010]',
|
||||||
|
'before:overflow-hidden',
|
||||||
|
'before:[clip-path:inset(0_0_0_0)]',
|
||||||
|
|
||||||
|
'after:content-[attr(data-text)]',
|
||||||
|
'after:absolute',
|
||||||
|
'after:top-0',
|
||||||
|
'after:text-white',
|
||||||
|
'after:bg-[#060010]',
|
||||||
|
'after:overflow-hidden',
|
||||||
|
'after:[clip-path:inset(0_0_0_0)]'
|
||||||
|
];
|
||||||
|
|
||||||
|
const normalGlitchClasses = [
|
||||||
|
// After pseudo-element for normal mode
|
||||||
|
'after:left-[10px]',
|
||||||
|
'after:[text-shadow:var(--after-shadow,-10px_0_red)]',
|
||||||
|
'after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]',
|
||||||
|
|
||||||
|
// Before pseudo-element for normal mode
|
||||||
|
'before:left-[-10px]',
|
||||||
|
'before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
|
||||||
|
'before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]'
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoverOnlyClasses = [
|
||||||
|
// Hide pseudo-elements by default
|
||||||
|
'before:content-[""]',
|
||||||
|
'before:opacity-0',
|
||||||
|
'before:[animation:none]',
|
||||||
|
'after:content-[""]',
|
||||||
|
'after:opacity-0',
|
||||||
|
'after:[animation:none]',
|
||||||
|
|
||||||
|
// Show and animate on hover
|
||||||
|
'hover:before:content-[attr(data-text)]',
|
||||||
|
'hover:before:opacity-100',
|
||||||
|
'hover:before:left-[-10px]',
|
||||||
|
'hover:before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
|
||||||
|
'hover:before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]',
|
||||||
|
|
||||||
|
'hover:after:content-[attr(data-text)]',
|
||||||
|
'hover:after:opacity-100',
|
||||||
|
'hover:after:left-[10px]',
|
||||||
|
'hover:after:[text-shadow:var(--after-shadow,-10px_0_red)]',
|
||||||
|
'hover:after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]'
|
||||||
|
];
|
||||||
|
|
||||||
const computedClasses = computed(() => {
|
const computedClasses = computed(() => {
|
||||||
const hoverClass = props.enableOnHover ? 'enable-on-hover' : '';
|
const classes = [...baseClasses];
|
||||||
return `glitch ${hoverClass} ${props.className}`.trim();
|
|
||||||
|
if (props.enableOnHover) {
|
||||||
|
classes.push(...hoverOnlyClasses);
|
||||||
|
} else {
|
||||||
|
classes.push(...normalGlitchClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.className) {
|
||||||
|
classes.push(props.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes.join(' ');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes animate-glitch {
|
||||||
|
0% {
|
||||||
|
clip-path: inset(20% 0 50% 0);
|
||||||
|
}
|
||||||
|
5% {
|
||||||
|
clip-path: inset(10% 0 60% 0);
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
clip-path: inset(15% 0 55% 0);
|
||||||
|
}
|
||||||
|
15% {
|
||||||
|
clip-path: inset(25% 0 35% 0);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
clip-path: inset(30% 0 40% 0);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
clip-path: inset(40% 0 20% 0);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
clip-path: inset(10% 0 60% 0);
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
clip-path: inset(15% 0 55% 0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
clip-path: inset(25% 0 35% 0);
|
||||||
|
}
|
||||||
|
45% {
|
||||||
|
clip-path: inset(30% 0 40% 0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
clip-path: inset(20% 0 50% 0);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
clip-path: inset(10% 0 60% 0);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
clip-path: inset(15% 0 55% 0);
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
clip-path: inset(25% 0 35% 0);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
clip-path: inset(30% 0 40% 0);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
clip-path: inset(40% 0 20% 0);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
clip-path: inset(20% 0 50% 0);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
clip-path: inset(10% 0 60% 0);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
clip-path: inset(15% 0 55% 0);
|
||||||
|
}
|
||||||
|
95% {
|
||||||
|
clip-path: inset(25% 0 35% 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
clip-path: inset(30% 0 40% 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user