init: 初始化 VitePress 官网项目
This commit is contained in:
68
docs/.vitepress/theme/components/CountUpNumber.vue
Normal file
68
docs/.vitepress/theme/components/CountUpNumber.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
interface Props {
|
||||
end: number
|
||||
duration?: number
|
||||
suffix?: string
|
||||
decimals?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
duration: 2000,
|
||||
suffix: '',
|
||||
decimals: 0,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
const refEl = ref<HTMLDivElement | null>(null)
|
||||
let hasAnimated = false
|
||||
let rafId: number | null = null
|
||||
let observer: IntersectionObserver | null = null
|
||||
|
||||
onMounted(() => {
|
||||
observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting && !hasAnimated) {
|
||||
hasAnimated = true
|
||||
let startTime: number | null = null
|
||||
|
||||
const animate = (currentTime: number) => {
|
||||
if (!startTime) startTime = currentTime
|
||||
const progress = Math.min((currentTime - startTime) / props.duration, 1)
|
||||
const easeProgress = 1 - Math.pow(1 - progress, 4)
|
||||
count.value = props.end * easeProgress
|
||||
|
||||
if (progress < 1) {
|
||||
rafId = requestAnimationFrame(animate)
|
||||
} else {
|
||||
count.value = props.end
|
||||
}
|
||||
}
|
||||
|
||||
rafId = requestAnimationFrame(animate)
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
)
|
||||
|
||||
if (refEl.value) {
|
||||
observer.observe(refEl.value)
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (observer && refEl.value) observer.disconnect()
|
||||
if (rafId) cancelAnimationFrame(rafId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="refEl"
|
||||
class="text-4xl md:text-5xl font-bold text-apple-dark mb-2 transition-transform group-hover:scale-110 duration-300 tabular-nums"
|
||||
>
|
||||
{{ count.toFixed(props.decimals) }}
|
||||
<span v-if="props.suffix" class="text-2xl ml-1 font-medium text-gray-400">{{ props.suffix }}</span>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user