|
1 | 1 | <script setup lang="ts"> |
2 | 2 | import { consola } from 'consola' |
| 3 | +import { animate, useMotionValue } from 'motion-v' |
3 | 4 |
|
4 | 5 | const { mapInstance } = useMapControls() |
5 | 6 | const { locationCount, clusterCount } = useVisibleLocations() |
6 | 7 | const hasVisibleFeatures = computed(() => locationCount.value > 0 || clusterCount.value > 0) |
7 | 8 | const count = ref<number | null>(null) |
8 | 9 | const loading = ref(false) |
9 | 10 |
|
| 11 | +// Animated count using motion value |
| 12 | +const animatedCount = useMotionValue(0) |
| 13 | +const displayCount = ref(0) |
| 14 | +
|
| 15 | +watch(count, (newCount) => { |
| 16 | + if (newCount === null) |
| 17 | + return |
| 18 | + animate(animatedCount, newCount, { |
| 19 | + duration: 0.4, |
| 20 | + ease: 'easeOut', |
| 21 | + onUpdate: (latest: number) => { |
| 22 | + displayCount.value = Math.round(latest) |
| 23 | + }, |
| 24 | + }) |
| 25 | +}, { immediate: true }) |
| 26 | +
|
10 | 27 | const updateCount = useDebounceFn(async () => { |
11 | 28 | if (!mapInstance.value) |
12 | 29 | return |
@@ -58,18 +75,33 @@ watch(mapInstance, (map) => { |
58 | 75 | > |
59 | 76 | <div |
60 | 77 | v-if="hasVisibleFeatures && count !== null && count > 0" |
61 | | - |
62 | 78 | flex pointer-events-none bottom-8 left-0 right-0 justify-center fixed z-10 |
63 | 79 | > |
64 | 80 | <div |
65 | 81 | bg="white/90 backdrop-blur" |
66 | 82 | text="neutral-700 f-xs" |
67 | | - |
68 | 83 | outline="~ 1.5 neutral/8 offset--1.5" |
69 | 84 | font-medium px-8 py-3 rounded-full pointer-events-auto shadow-lg |
70 | 85 | > |
71 | | - There are {{ count }} locations in this area |
| 86 | + There are <span align-middle inline-flex h-16 items-center overflow-hidden><span :key="displayCount" class="animate-slide-in" inline-block tabular-nums>{{ displayCount }}</span></span> locations in this area |
72 | 87 | </div> |
73 | 88 | </div> |
74 | 89 | </Transition> |
75 | 90 | </template> |
| 91 | + |
| 92 | +<style scoped> |
| 93 | +.animate-slide-in { |
| 94 | + animation: slideIn 0.3s ease-out; |
| 95 | +} |
| 96 | +
|
| 97 | +@keyframes slideIn { |
| 98 | + from { |
| 99 | + opacity: 0; |
| 100 | + transform: translateY(100%); |
| 101 | + } |
| 102 | + to { |
| 103 | + opacity: 1; |
| 104 | + transform: translateY(0); |
| 105 | + } |
| 106 | +} |
| 107 | +</style> |
0 commit comments