Skip to content

Commit 7965e70

Browse files
committed
feat: add effect to like button
1 parent a170292 commit 7965e70

File tree

1 file changed

+126
-20
lines changed

1 file changed

+126
-20
lines changed

app/components/Package/Header.vue

Lines changed: 126 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ onKeyStroke(
184184
// TODO: Maybe set this where it's not loaded here every load?
185185
const { user } = useAtproto()
186186
187+
const likeAnimKey = shallowRef(0)
188+
const showLikeFloat = shallowRef(false)
189+
190+
const heartAnimStyle = computed(() => {
191+
if (likeAnimKey.value === 0) return {}
192+
return {
193+
animation: likesData.value?.userHasLiked
194+
? 'heart-spring 0.55s cubic-bezier(0.34,1.56,0.64,1) forwards'
195+
: 'heart-unlike 0.3s ease forwards',
196+
}
197+
})
198+
187199
const authModal = useModal('auth-modal')
188200
189201
const { data: likesData, status: likeStatus } = useFetch(
@@ -211,6 +223,15 @@ const likeAction = async () => {
211223
const currentlyLiked = likesData.value?.userHasLiked ?? false
212224
const currentLikes = likesData.value?.totalLikes ?? 0
213225
226+
likeAnimKey.value++
227+
228+
if (!currentlyLiked) {
229+
showLikeFloat.value = true
230+
setTimeout(() => {
231+
showLikeFloat.value = false
232+
}, 850)
233+
}
234+
214235
// Optimistic update
215236
likesData.value = {
216237
totalLikes: currentlyLiked ? currentLikes - 1 : currentLikes + 1,
@@ -293,26 +314,37 @@ const likeAction = async () => {
293314
class="items-center"
294315
strategy="fixed"
295316
>
296-
<ButtonBase
297-
@click="likeAction"
298-
size="medium"
299-
:aria-label="
300-
likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
301-
"
302-
:aria-pressed="likesData?.userHasLiked"
303-
:classicon="
304-
likesData?.userHasLiked ? 'i-lucide:heart-minus text-red-500' : 'i-lucide:heart-plus'
305-
"
306-
>
307-
<span
308-
v-if="isLoadingLikeData"
309-
class="i-svg-spinners:ring-resize w-3 h-3 my-0.5"
310-
aria-hidden="true"
311-
/>
312-
<span v-else>
313-
{{ compactNumberFormatter.format(likesData?.totalLikes ?? 0) }}
314-
</span>
315-
</ButtonBase>
317+
<div :class="$style.likeWrapper">
318+
<span v-if="showLikeFloat" aria-hidden="true" :class="$style.likeFloat">+1</span>
319+
<ButtonBase
320+
@click="likeAction"
321+
size="medium"
322+
:aria-label="
323+
likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
324+
"
325+
:aria-pressed="likesData?.userHasLiked"
326+
>
327+
<span
328+
:key="likeAnimKey"
329+
:class="
330+
likesData?.userHasLiked
331+
? 'i-lucide:heart-minus fill-red-500 text-red-500'
332+
: 'i-lucide:heart-plus'
333+
"
334+
:style="heartAnimStyle"
335+
aria-hidden="true"
336+
class="inline-block w-4 h-4"
337+
/>
338+
<span
339+
v-if="isLoadingLikeData"
340+
class="i-svg-spinners:ring-resize w-3 h-3 my-0.5"
341+
aria-hidden="true"
342+
/>
343+
<span v-else>
344+
{{ compactNumberFormatter.format(likesData?.totalLikes ?? 0) }}
345+
</span>
346+
</ButtonBase>
347+
</div>
316348
</TooltipApp>
317349
</div>
318350
</div>
@@ -458,4 +490,78 @@ const likeAction = async () => {
458490
display: none;
459491
}
460492
}
493+
494+
.likeWrapper {
495+
position: relative;
496+
display: inline-flex;
497+
}
498+
499+
.likeFloat {
500+
position: absolute;
501+
top: 0;
502+
left: 50%;
503+
font-size: 12px;
504+
font-weight: 600;
505+
color: var(--color-red-500, #ef4444);
506+
pointer-events: none;
507+
white-space: nowrap;
508+
animation: float-up 0.75s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
509+
}
510+
511+
@keyframes float-up {
512+
0% {
513+
opacity: 0;
514+
transform: translateX(-50%) translateY(0);
515+
}
516+
15% {
517+
opacity: 1;
518+
transform: translateX(-50%) translateY(-4px);
519+
}
520+
80% {
521+
opacity: 1;
522+
transform: translateX(-50%) translateY(-20px);
523+
}
524+
100% {
525+
opacity: 0;
526+
transform: translateX(-50%) translateY(-28px);
527+
}
528+
}
529+
</style>
530+
531+
<style>
532+
@keyframes heart-spring {
533+
0% {
534+
transform: scale(1);
535+
}
536+
15% {
537+
transform: scale(0.78);
538+
}
539+
45% {
540+
transform: scale(1.55);
541+
}
542+
65% {
543+
transform: scale(0.93);
544+
}
545+
80% {
546+
transform: scale(1.1);
547+
}
548+
100% {
549+
transform: scale(1);
550+
}
551+
}
552+
553+
@keyframes heart-unlike {
554+
0% {
555+
transform: scale(1);
556+
}
557+
30% {
558+
transform: scale(0.85);
559+
}
560+
60% {
561+
transform: scale(1.05);
562+
}
563+
100% {
564+
transform: scale(1);
565+
}
566+
}
461567
</style>

0 commit comments

Comments
 (0)