feat(layout): 重构布局组件体系与数据层
- 重构 Header 导航、Footer 页脚、MobileMenu 移动端菜单 - 新增 MobileTabBar 移动端底部导航栏 - 更新 MegaDropdown 大型下拉导航 - 重构 SEO 结构化数据组件 - 更新数据层常量 (products, services, solutions, navigation 等) - 新增 useCountUp 数字递增 Hook - 修复 .gitignore 中 src/lib 被错误忽略的问题
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
|
||||
interface UseCountUpOptions {
|
||||
end: number;
|
||||
duration?: number;
|
||||
start?: number;
|
||||
decimals?: number;
|
||||
easing?: (t: number) => number;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);
|
||||
|
||||
export function useCountUp({
|
||||
end,
|
||||
duration = 1800,
|
||||
start = 0,
|
||||
decimals = 0,
|
||||
easing = easeOutCubic,
|
||||
enabled = true,
|
||||
}: UseCountUpOptions) {
|
||||
const [value, setValue] = useState(start);
|
||||
const startTimeRef = useRef<number | null>(null);
|
||||
const frameRef = useRef<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
setValue(end);
|
||||
return;
|
||||
}
|
||||
|
||||
setValue(start);
|
||||
startTimeRef.current = null;
|
||||
|
||||
const animate = (now: number) => {
|
||||
if (startTimeRef.current === null) {
|
||||
startTimeRef.current = now;
|
||||
}
|
||||
|
||||
const progress = Math.min((now - startTimeRef.current) / duration, 1);
|
||||
const eased = easing(progress);
|
||||
const current = start + (end - start) * eased;
|
||||
|
||||
setValue(Number(current.toFixed(decimals)));
|
||||
|
||||
if (progress < 1) {
|
||||
frameRef.current = requestAnimationFrame(animate);
|
||||
} else {
|
||||
setValue(end);
|
||||
}
|
||||
};
|
||||
|
||||
frameRef.current = requestAnimationFrame(animate);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frameRef.current);
|
||||
};
|
||||
}, [end, duration, start, decimals, easing, enabled]);
|
||||
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user