mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 14:39:30 -07:00
fix: clean up code formatting and improve hover effect styling
This commit is contained in:
@@ -10,17 +10,15 @@
|
|||||||
v-for="(src, idx) in images"
|
v-for="(src, idx) in images"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
ref="cardRefs"
|
ref="cardRefs"
|
||||||
class="absolute w-[200px] aspect-square border-[5px] border-white rounded-[25px] overflow-hidden shadow-[0_4px_10px_rgba(0,0,0,0.2)] bg-[#f8f9fa] opacity-0"
|
class="absolute w-[200px] aspect-square border-[5px] border-white rounded-[25px] overflow-hidden shadow-[0_4px_10px_rgba(0,0,0,0.2)] bg-[#0b0b0b] opacity-0"
|
||||||
:style="{ transform: transformStyles[idx] ?? 'none' }"
|
:style="{ transform: transformStyles[idx] ?? 'none' }"
|
||||||
@mouseenter="() => pushSiblings(idx)"
|
@mouseenter="() => pushSiblings(idx)"
|
||||||
@mouseleave="resetSiblings"
|
@mouseleave="resetSiblings"
|
||||||
>
|
>
|
||||||
<div v-if="!imageLoaded[idx]" class="absolute inset-0 z-[1] flex items-center justify-center bg-black/80">
|
<div v-if="!imageLoaded[idx]" class="absolute inset-0 z-[1] bg-[#0b0b0b] overflow-hidden shimmer-container"></div>
|
||||||
<div class="w-[75px] h-[75px] border-[3px] border-gray-400 border-t-[#27FF64] rounded-full animate-spin"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<img
|
<img
|
||||||
class="absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ease-in-out z-[2]"
|
class="absolute inset-0 w-full h-full object-cover z-[2] transition-opacity duration-700 ease-out"
|
||||||
:src="src"
|
:src="src"
|
||||||
:alt="`card-${idx}`"
|
:alt="`card-${idx}`"
|
||||||
:style="{ opacity: imageLoaded[idx] ? 1 : 0 }"
|
:style="{ opacity: imageLoaded[idx] ? 1 : 0 }"
|
||||||
@@ -165,13 +163,34 @@ const playEntranceAnimation = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(playEntranceAnimation);
|
onMounted(playEntranceAnimation);
|
||||||
watch(() => props.images, async () => {
|
watch(
|
||||||
|
() => props.images,
|
||||||
|
async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
gsap.set(cardRefs.value, { opacity: 0, scale: 0 });
|
gsap.set(cardRefs.value, { opacity: 0, scale: 0 });
|
||||||
playEntranceAnimation();
|
playEntranceAnimation();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
gsap.killTweensOf(cardRefs.value);
|
gsap.killTweensOf(cardRefs.value);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.shimmer-container {
|
||||||
|
background: linear-gradient(110deg, transparent 40%, rgba(255, 255, 255, 0.1) 50%, transparent 60%);
|
||||||
|
background-size: 600% 600%;
|
||||||
|
background-position: -600% 0;
|
||||||
|
animation: shimmer-sweep 6s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer-sweep {
|
||||||
|
0% {
|
||||||
|
background-position: -600% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -26,12 +26,11 @@
|
|||||||
>
|
>
|
||||||
{{ letter }}
|
{{ letter }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span v-if="wordIndex < words.length - 1" class="inline-block"> </span>
|
||||||
v-if="wordIndex < words.length - 1"
|
</span>
|
||||||
class="inline-block"
|
<span class="absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap clip-rect-0 border-0">
|
||||||
> </span>
|
{{ props.label }}
|
||||||
</span>
|
</span>
|
||||||
<span class="absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap clip-rect-0 border-0">{{ props.label }}</span>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -57,7 +56,7 @@ const props = withDefaults(defineProps<VariableProximityProps>(), {
|
|||||||
falloff: 'linear',
|
falloff: 'linear',
|
||||||
className: '',
|
className: '',
|
||||||
style: () => ({}),
|
style: () => ({}),
|
||||||
onClick: undefined,
|
onClick: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const rootRef = ref<HTMLElement | null>(null);
|
const rootRef = ref<HTMLElement | null>(null);
|
||||||
@@ -90,7 +89,7 @@ const parsedSettings = computed(() => {
|
|||||||
return Array.from(fromSettings.entries()).map(([axis, fromValue]) => ({
|
return Array.from(fromSettings.entries()).map(([axis, fromValue]) => ({
|
||||||
axis,
|
axis,
|
||||||
fromValue,
|
fromValue,
|
||||||
toValue: toSettings.get(axis) ?? fromValue,
|
toValue: toSettings.get(axis) ?? fromValue
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,15 +99,17 @@ const calculateDistance = (x1: number, y1: number, x2: number, y2: number) =>
|
|||||||
const calculateFalloff = (distance: number) => {
|
const calculateFalloff = (distance: number) => {
|
||||||
const norm = Math.min(Math.max(1 - distance / props.radius, 0), 1);
|
const norm = Math.min(Math.max(1 - distance / props.radius, 0), 1);
|
||||||
switch (props.falloff) {
|
switch (props.falloff) {
|
||||||
case 'exponential': return norm ** 2;
|
case 'exponential':
|
||||||
case 'gaussian': return Math.exp(-((distance / (props.radius / 2)) ** 2) / 2);
|
return norm ** 2;
|
||||||
|
case 'gaussian':
|
||||||
|
return Math.exp(-((distance / (props.radius / 2)) ** 2) / 2);
|
||||||
case 'linear':
|
case 'linear':
|
||||||
default: return norm;
|
default:
|
||||||
|
return norm;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLetterKey = (wordIndex: number, letterIndex: number) =>
|
const getLetterKey = (wordIndex: number, letterIndex: number) => `${wordIndex}-${letterIndex}`;
|
||||||
`${wordIndex}-${letterIndex}`;
|
|
||||||
|
|
||||||
const getGlobalLetterIndex = (wordIndex: number, letterIndex: number) => {
|
const getGlobalLetterIndex = (wordIndex: number, letterIndex: number) => {
|
||||||
let globalIndex = 0;
|
let globalIndex = 0;
|
||||||
@@ -175,12 +176,7 @@ const animationLoop = () => {
|
|||||||
const letterCenterX = rect.left + rect.width / 2 - containerRect.left;
|
const letterCenterX = rect.left + rect.width / 2 - containerRect.left;
|
||||||
const letterCenterY = rect.top + rect.height / 2 - containerRect.top;
|
const letterCenterY = rect.top + rect.height / 2 - containerRect.top;
|
||||||
|
|
||||||
const distance = calculateDistance(
|
const distance = calculateDistance(mousePosition.value.x, mousePosition.value.y, letterCenterX, letterCenterY);
|
||||||
mousePosition.value.x,
|
|
||||||
mousePosition.value.y,
|
|
||||||
letterCenterX,
|
|
||||||
letterCenterY
|
|
||||||
);
|
|
||||||
|
|
||||||
if (distance >= props.radius) {
|
if (distance >= props.radius) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TabbedLayout>
|
<TabbedLayout>
|
||||||
<template #preview>
|
<template #preview>
|
||||||
@@ -18,26 +17,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Customize>
|
<Customize>
|
||||||
<PreviewSwitch
|
<PreviewSwitch title="Enable Hover Effect" v-model="enableHover" />
|
||||||
title="Enable Hover Effect"
|
|
||||||
v-model="enableHover"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PreviewSlider
|
<PreviewSlider title="Animation Delay" v-model="animationDelay" :min="0.1" :max="2" :step="0.1" />
|
||||||
title="Animation Delay"
|
|
||||||
v-model="animationDelay"
|
|
||||||
:min="0.1"
|
|
||||||
:max="2"
|
|
||||||
:step="0.1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PreviewSlider
|
<PreviewSlider title="Animation Stagger" v-model="animationStagger" :min="0" :max="0.3" :step="0.01" />
|
||||||
title="Animation Stagger"
|
|
||||||
v-model="animationStagger"
|
|
||||||
:min="0"
|
|
||||||
:max="0.3"
|
|
||||||
:step="0.01"
|
|
||||||
/>
|
|
||||||
</Customize>
|
</Customize>
|
||||||
|
|
||||||
<PropTable :data="propData" />
|
<PropTable :data="propData" />
|
||||||
@@ -76,11 +60,11 @@ const animationStagger = ref(0.08);
|
|||||||
const { rerenderKey, forceRerender } = useForceRerender();
|
const { rerenderKey, forceRerender } = useForceRerender();
|
||||||
|
|
||||||
const images = ref([
|
const images = ref([
|
||||||
'https://picsum.photos/400/400?grayscale',
|
'https://picsum.photos/id/287/300/300?grayscale',
|
||||||
'https://picsum.photos/500/500?grayscale',
|
'https://picsum.photos/id/1001/300/300?grayscale',
|
||||||
'https://picsum.photos/600/600?grayscale',
|
'https://picsum.photos/id/1027/300/300?grayscale',
|
||||||
'https://picsum.photos/700/700?grayscale',
|
'https://picsum.photos/id/1025/300/300?grayscale',
|
||||||
'https://picsum.photos/300/300?grayscale'
|
'https://picsum.photos/id/1026/300/300?grayscale'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const transformStyles = ref([
|
const transformStyles = ref([
|
||||||
@@ -126,7 +110,7 @@ const propData = [
|
|||||||
name: 'animationStagger',
|
name: 'animationStagger',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: '-',
|
default: '-',
|
||||||
description: 'Time (in seconds) between each card\'s animation.'
|
description: "Time (in seconds) between each card's animation."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'easeType',
|
name: 'easeType',
|
||||||
@@ -144,7 +128,7 @@ const propData = [
|
|||||||
name: 'enableHover',
|
name: 'enableHover',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: 'false',
|
default: 'false',
|
||||||
description: 'If true, hovering pushes siblings aside and flattens the hovered card\'s rotation.'
|
description: "If true, hovering pushes siblings aside and flattens the hovered card's rotation."
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,13 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Customize>
|
<Customize>
|
||||||
<PreviewSlider
|
<PreviewSlider title="Radius" v-model="radius" :min="50" :max="300" :step="10" />
|
||||||
title="Radius"
|
|
||||||
v-model="radius"
|
|
||||||
:min="50"
|
|
||||||
:max="300"
|
|
||||||
:step="10"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="flex gap-2 flex-wrap">
|
<div class="flex gap-2 flex-wrap">
|
||||||
<button
|
<button
|
||||||
@@ -80,13 +74,13 @@ const propData = [
|
|||||||
{
|
{
|
||||||
name: 'fromFontVariationSettings',
|
name: 'fromFontVariationSettings',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '"\'wght\' 400, \'opsz\' 9"',
|
default: "\"'wght' 400, 'opsz' 9\"",
|
||||||
description: 'The starting variation settings.'
|
description: 'The starting variation settings.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'toFontVariationSettings',
|
name: 'toFontVariationSettings',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '"\'wght\' 800, \'opsz\' 40"',
|
default: "\"'wght' 800, 'opsz' 40\"",
|
||||||
description: 'The variation settings to reach at cursor proximity.'
|
description: 'The variation settings to reach at cursor proximity.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -128,5 +122,5 @@ const propData = [
|
|||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap");
|
@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap');
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user