feat: 小龍蝦載入動畫 + HostAggregator 效能優化
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:
OG T
2026-04-08 22:44:24 +08:00
parent 6f475000f6
commit c669069427
3 changed files with 106 additions and 9 deletions

View 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