Skip to content

Commit 67bae8e

Browse files
committed
refactor(style): utilize tailwind instead of CSS
1 parent 12f72fe commit 67bae8e

File tree

1 file changed

+32
-109
lines changed

1 file changed

+32
-109
lines changed
Lines changed: 32 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,29 @@
11
<template>
22
<div
3-
:class="['bounceCardsContainer', className]"
3+
:class="['relative flex items-center justify-center', className]"
44
:style="{
5-
position: 'relative',
65
width: typeof containerWidth === 'number' ? `${containerWidth}px` : containerWidth,
7-
height: typeof containerHeight === 'number' ? `${containerHeight}px` : containerHeight,
8-
display: 'flex',
9-
justifyContent: 'center',
10-
alignItems: 'center'
6+
height: typeof containerHeight === 'number' ? `${containerHeight}px` : containerHeight
117
}"
128
>
139
<div
1410
v-for="(src, idx) in images"
1511
:key="idx"
16-
:class="`card card-${idx}`"
17-
:style="{
18-
position: 'absolute',
19-
width: '200px',
20-
aspectRatio: '1',
21-
border: '5px solid #fff',
22-
borderRadius: '25px',
23-
overflow: 'hidden',
24-
boxShadow: '0 4px 10px rgba(0, 0, 0, 0.2)',
25-
transform: transformStyles[idx] ?? 'none',
26-
backgroundColor: '#f8f9fa'
27-
}"
12+
ref="cardRefs"
13+
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"
14+
:style="{ transform: transformStyles[idx] ?? 'none' }"
2815
@mouseenter="() => pushSiblings(idx)"
2916
@mouseleave="resetSiblings"
3017
>
31-
<div
32-
v-if="!imageLoaded[idx]"
33-
class="placeholder"
34-
:style="{
35-
position: 'absolute',
36-
top: '0',
37-
left: '0',
38-
width: '100%',
39-
height: '100%',
40-
backgroundColor: 'rgba(0, 0, 0, 0.8)',
41-
display: 'flex',
42-
alignItems: 'center',
43-
justifyContent: 'center',
44-
zIndex: 1
45-
}"
46-
>
47-
<div
48-
class="loading-spinner"
49-
:style="{
50-
width: '75px',
51-
height: '75px',
52-
border: '3px solid #a3a3a3',
53-
borderTop: '3px solid #27FF64',
54-
borderRadius: '50%',
55-
}"
56-
></div>
18+
<div v-if="!imageLoaded[idx]" class="absolute inset-0 z-[1] flex items-center justify-center bg-black/80">
19+
<div class="w-[75px] h-[75px] border-[3px] border-gray-400 border-t-[#27FF64] rounded-full animate-spin"></div>
5720
</div>
5821

5922
<img
60-
class="image"
23+
class="absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ease-in-out z-[2]"
6124
:src="src"
6225
:alt="`card-${idx}`"
63-
:style="{
64-
position: 'absolute',
65-
top: '0',
66-
left: '0',
67-
width: '100%',
68-
height: '100%',
69-
objectFit: 'cover',
70-
opacity: imageLoaded[idx] ? 1 : 0,
71-
transition: 'opacity 0.3s ease',
72-
zIndex: 2
73-
}"
26+
:style="{ opacity: imageLoaded[idx] ? 1 : 0 }"
7427
@load="() => onImageLoad(idx)"
7528
@error="() => onImageError(idx)"
7629
/>
@@ -79,7 +32,7 @@
7932
</template>
8033

8134
<script setup lang="ts">
82-
import { onMounted, onUnmounted, ref } from 'vue';
35+
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
8336
import { gsap } from 'gsap';
8437
8538
export interface BounceCardsProps {
@@ -113,6 +66,7 @@ const props = withDefaults(defineProps<BounceCardsProps>(), {
11366
});
11467
11568
const imageLoaded = ref(new Array(props.images.length).fill(false));
69+
const cardRefs = ref<HTMLElement[]>([]);
11670
11771
const getNoRotationTransform = (transformStr: string): string => {
11872
const hasRotate = /rotate\([\s\S]*?\)/.test(transformStr);
@@ -133,23 +87,21 @@ const getPushedTransform = (baseTransform: string, offsetX: number): string => {
13387
const newX = currentX + offsetX;
13488
return baseTransform.replace(translateRegex, `translate(${newX}px)`);
13589
} else {
136-
return baseTransform === 'none'
137-
? `translate(${offsetX}px)`
138-
: `${baseTransform} translate(${offsetX}px)`;
90+
return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;
13991
}
14092
};
14193
14294
const pushSiblings = (hoveredIdx: number) => {
14395
if (!props.enableHover) return;
14496
14597
props.images.forEach((_, i) => {
146-
gsap.killTweensOf(`.card-${i}`);
98+
gsap.killTweensOf(cardRefs.value[i]);
14799
148100
const baseTransform = props.transformStyles[i] || 'none';
149101
150102
if (i === hoveredIdx) {
151103
const noRotationTransform = getNoRotationTransform(baseTransform);
152-
gsap.to(`.card-${i}`, {
104+
gsap.to(cardRefs.value[i], {
153105
transform: noRotationTransform,
154106
duration: 0.4,
155107
ease: 'back.out(1.4)',
@@ -158,11 +110,10 @@ const pushSiblings = (hoveredIdx: number) => {
158110
} else {
159111
const offsetX = i < hoveredIdx ? -160 : 160;
160112
const pushedTransform = getPushedTransform(baseTransform, offsetX);
161-
162113
const distance = Math.abs(hoveredIdx - i);
163114
const delay = distance * 0.05;
164115
165-
gsap.to(`.card-${i}`, {
116+
gsap.to(cardRefs.value[i], {
166117
transform: pushedTransform,
167118
duration: 0.4,
168119
ease: 'back.out(1.4)',
@@ -177,9 +128,9 @@ const resetSiblings = () => {
177128
if (!props.enableHover) return;
178129
179130
props.images.forEach((_, i) => {
180-
gsap.killTweensOf(`.card-${i}`);
131+
gsap.killTweensOf(cardRefs.value[i]);
181132
const baseTransform = props.transformStyles[i] || 'none';
182-
gsap.to(`.card-${i}`, {
133+
gsap.to(cardRefs.value[i], {
183134
transform: baseTransform,
184135
duration: 0.4,
185136
ease: 'back.out(1.4)',
@@ -196,59 +147,31 @@ const onImageError = (idx: number) => {
196147
imageLoaded.value[idx] = true;
197148
};
198149
199-
onMounted(() => {
150+
const playEntranceAnimation = () => {
151+
gsap.killTweensOf(cardRefs.value);
152+
gsap.set(cardRefs.value, { opacity: 0, scale: 0 });
153+
200154
gsap.fromTo(
201-
'.card',
202-
{ scale: 0 },
155+
cardRefs.value,
156+
{ scale: 0, opacity: 0 },
203157
{
204158
scale: 1,
159+
opacity: 1,
205160
stagger: props.animationStagger,
206161
ease: props.easeType,
207162
delay: props.animationDelay
208163
}
209164
);
165+
};
166+
167+
onMounted(playEntranceAnimation);
168+
watch(() => props.images, async () => {
169+
await nextTick();
170+
gsap.set(cardRefs.value, { opacity: 0, scale: 0 });
171+
playEntranceAnimation();
210172
});
211173
212174
onUnmounted(() => {
213-
gsap.killTweensOf('.card');
214-
props.images.forEach((_, i) => {
215-
gsap.killTweensOf(`.card-${i}`);
216-
});
175+
gsap.killTweensOf(cardRefs.value);
217176
});
218177
</script>
219-
220-
<style scoped>
221-
.bounceCardsContainer {
222-
position: relative;
223-
display: flex;
224-
justify-content: center;
225-
align-items: center;
226-
width: 400px;
227-
height: 400px;
228-
}
229-
230-
.card {
231-
position: absolute;
232-
width: 200px;
233-
aspect-ratio: 1;
234-
border: 5px solid #fff;
235-
border-radius: 25px;
236-
overflow: hidden;
237-
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
238-
background-color: transparent !important;
239-
}
240-
241-
.card .image {
242-
width: 100%;
243-
height: 100%;
244-
object-fit: cover;
245-
}
246-
247-
.loading-spinner {
248-
animation: spin 1s linear infinite;
249-
}
250-
@keyframes spin {
251-
0% { transform: rotate(0deg);}
252-
100% { transform: rotate(360deg);}
253-
}
254-
</style>

0 commit comments

Comments
 (0)