mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-17 01:33:43 +08:00
feat: Enhance SearchLoadingAnimation with pause functionality and progress indicators; update Badge component to support icons with customizable positions
This commit is contained in:
@@ -1,29 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||
|
||||
interface SearchLoadingAnimationProps {
|
||||
currentSource?: string;
|
||||
checkedSources?: number;
|
||||
totalSources?: number;
|
||||
isPaused?: boolean;
|
||||
onComplete?: (checkedSources: number, totalSources: number) => void;
|
||||
}
|
||||
|
||||
export function SearchLoadingAnimation({
|
||||
currentSource,
|
||||
checkedSources = 0,
|
||||
totalSources = 16,
|
||||
isPaused = false,
|
||||
onComplete,
|
||||
}: SearchLoadingAnimationProps) {
|
||||
const [dots, setDots] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const dotInterval = setInterval(() => {
|
||||
setDots((prev) => (prev.length >= 3 ? '' : prev + '.'));
|
||||
}, 500);
|
||||
return () => clearInterval(dotInterval);
|
||||
}, []);
|
||||
const dotIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const hasCalledComplete = useRef(false);
|
||||
|
||||
// Calculate progress (0-100%)
|
||||
const progress = totalSources > 0 ? (checkedSources / totalSources) * 100 : 0;
|
||||
const isComplete = progress >= 100;
|
||||
|
||||
// Animation pause/resume logic
|
||||
useEffect(() => {
|
||||
if (isPaused || isComplete) {
|
||||
if (dotIntervalRef.current) {
|
||||
clearInterval(dotIntervalRef.current);
|
||||
dotIntervalRef.current = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dotIntervalRef.current = setInterval(() => {
|
||||
setDots((prev) => (prev.length >= 3 ? '' : prev + '.'));
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
if (dotIntervalRef.current) {
|
||||
clearInterval(dotIntervalRef.current);
|
||||
dotIntervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [isPaused, isComplete]);
|
||||
|
||||
// Call onComplete callback when animation finishes
|
||||
useEffect(() => {
|
||||
if (isComplete && onComplete && !hasCalledComplete.current) {
|
||||
hasCalledComplete.current = true;
|
||||
// Small delay to allow animation to settle
|
||||
const timeout = setTimeout(() => {
|
||||
onComplete(checkedSources, totalSources);
|
||||
}, 300);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [isComplete, onComplete, checkedSources, totalSources]);
|
||||
|
||||
const statusText = `${checkedSources}/${totalSources} 个源`;
|
||||
|
||||
return (
|
||||
@@ -55,26 +90,40 @@ export function SearchLoadingAnimation({
|
||||
className="h-1 bg-[color-mix(in_srgb,var(--glass-bg)_50%,transparent)] overflow-hidden rounded-[var(--radius-full)]"
|
||||
>
|
||||
<div
|
||||
className="h-full bg-[var(--accent-color)] transition-all duration-500 ease-out relative will-change-[width] rounded-[var(--radius-full)]"
|
||||
className="h-full bg-[var(--accent-color)] transition-all duration-500 ease-out relative will-change-transform rounded-[var(--radius-full)]"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
transform: 'translateZ(0)'
|
||||
}}
|
||||
>
|
||||
{/* Shimmer Effect - Optimized for GPU */}
|
||||
{/* Shimmer Effect - Optimized for GPU with contain for better performance */}
|
||||
<div
|
||||
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-shimmer"
|
||||
className="absolute inset-0 animate-shimmer"
|
||||
style={{
|
||||
background: 'linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%)',
|
||||
willChange: 'transform',
|
||||
transform: 'translateZ(0)'
|
||||
transform: 'translateZ(0)',
|
||||
contain: 'strict'
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Info - Real-time count */}
|
||||
{/* Progress Info - Real-time count with pause indicator */}
|
||||
<div className="flex items-center justify-between mt-2 text-xs text-[var(--text-color-secondary)]">
|
||||
<span>{statusText}</span>
|
||||
<span className="flex items-center gap-2">
|
||||
{statusText}
|
||||
{isPaused && (
|
||||
<span className="px-2 py-0.5 rounded-[var(--radius-full)] bg-[var(--glass-bg)] text-[10px]">
|
||||
已暂停
|
||||
</span>
|
||||
)}
|
||||
{isComplete && (
|
||||
<span className="px-2 py-0.5 rounded-[var(--radius-full)] bg-[var(--accent-color)] text-white text-[10px]">
|
||||
完成
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="font-medium">{Math.round(progress)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user