feat: 小龍蝦載入動畫 + HostAggregator 效能優化
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
前端: - LobsterLoading 共用元件 (Q版龍蝦上下浮動 + 文字提示) - 替換首頁所有「載入中...」為小龍蝦動畫 - PageTabs 骨架屏也換成龍蝦 後端: - TCP probe timeout: 3.0s → 1.5s - HTTP probe timeout: 5.0s → 2.0s - 30 秒記憶體快取 (避免 unreachable 主機拖慢前端) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
79
apps/web/src/components/shared/lobster-loading.tsx
Normal file
79
apps/web/src/components/shared/lobster-loading.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* LobsterLoading — 小龍蝦載入動畫
|
||||
* =================================
|
||||
* Sprint 5: 所有「載入中」用小龍蝦動畫取代純文字
|
||||
* 設計: Q版龍蝦上下浮動 + 文字提示
|
||||
*
|
||||
* 建立時間: 2026-04-08 (台北時區)
|
||||
*/
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
interface LobsterLoadingProps {
|
||||
/** 自訂提示文字 (預設 '載入中...') */
|
||||
text?: string
|
||||
/** 大小: 'sm' (24px) | 'md' (36px) | 'lg' (48px) */
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
}
|
||||
|
||||
const SIZES = {
|
||||
sm: { svg: 24, bob: 2, fontSize: 11 },
|
||||
md: { svg: 36, bob: 3, fontSize: 12 },
|
||||
lg: { svg: 48, bob: 4, fontSize: 13 },
|
||||
}
|
||||
|
||||
export function LobsterLoading({ text, size = 'md' }: LobsterLoadingProps) {
|
||||
const tc = useTranslations('common')
|
||||
const s = SIZES[size]
|
||||
const displayText = text ?? tc('loading')
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
|
||||
padding: size === 'lg' ? 48 : size === 'md' ? 32 : 16,
|
||||
gap: 8,
|
||||
}}>
|
||||
<style>{`
|
||||
@keyframes lobster-loading-bob {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-${s.bob}px); }
|
||||
}
|
||||
@keyframes lobster-loading-wave {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
`}</style>
|
||||
<div style={{ animation: 'lobster-loading-bob 1.2s ease-in-out infinite' }}>
|
||||
<svg width={s.svg} height={Math.round(s.svg * 20 / 18)} viewBox="0 0 18 20" fill="none">
|
||||
<ellipse cx="9" cy="13" rx="5.5" ry="6.5" fill="#E85530" opacity="0.9" />
|
||||
<circle cx="9" cy="7.5" r="4.5" fill="#E85530" opacity="0.9" />
|
||||
<circle cx="7" cy="6.5" r="1" fill="#b03a1a" />
|
||||
<circle cx="11" cy="6.5" r="1" fill="#b03a1a" />
|
||||
{/* 左鉗 */}
|
||||
<path d="M3.5 10 Q1 9 1.5 12 Q2 14 4 13" stroke="#E85530" strokeWidth="1.2" fill="none" strokeLinecap="round" />
|
||||
<ellipse cx="1.5" cy="12" rx="1.2" ry="1.5" fill="#E85530" opacity="0.7" transform="rotate(-10 1.5 12)" />
|
||||
{/* 右鉗 */}
|
||||
<path d="M14.5 10 Q17 9 16.5 12 Q16 14 14 13" stroke="#E85530" strokeWidth="1.2" fill="none" strokeLinecap="round" />
|
||||
<ellipse cx="16.5" cy="12" rx="1.2" ry="1.5" fill="#E85530" opacity="0.7" transform="rotate(10 16.5 12)" />
|
||||
{/* 觸角 */}
|
||||
<path d="M7 3 Q5 1 3 2" stroke="#b03a1a" strokeWidth="0.8" fill="none" strokeLinecap="round" />
|
||||
<path d="M11 3 Q13 1 15 2" stroke="#b03a1a" strokeWidth="0.8" fill="none" strokeLinecap="round" />
|
||||
{/* 尾巴 */}
|
||||
<path d="M6 19 Q9 21 12 19" stroke="#E85530" strokeWidth="1.2" fill="none" strokeLinecap="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: s.fontSize,
|
||||
color: '#87867f',
|
||||
fontFamily: "'DM Mono', monospace",
|
||||
animation: 'lobster-loading-wave 2s ease-in-out infinite',
|
||||
}}>
|
||||
{displayText}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LobsterLoading
|
||||
Reference in New Issue
Block a user