dramaling-vocab-learning/frontend/components/AudioPlayer.tsx

102 lines
2.9 KiB
TypeScript

'use client';
import { useState } from 'react';
export interface AudioPlayerProps {
text: string;
lang?: string;
onPlayStart?: () => void;
onPlayEnd?: () => void;
onError?: (error: string) => void;
className?: string;
disabled?: boolean;
}
export default function AudioPlayer({
text,
lang = 'en-US',
onPlayStart,
onPlayEnd,
onError,
className = '',
disabled = false
}: AudioPlayerProps) {
const [isPlaying, setIsPlaying] = useState(false);
// TTS播放控制功能
const toggleTTS = () => {
if (!('speechSynthesis' in window)) {
onError?.('您的瀏覽器不支援語音播放');
return;
}
// 如果正在播放,則停止
if (isPlaying) {
speechSynthesis.cancel();
setIsPlaying(false);
onPlayEnd?.();
return;
}
// 開始播放
speechSynthesis.cancel();
setIsPlaying(true);
onPlayStart?.();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = lang;
utterance.rate = 0.8; // 稍慢語速
utterance.pitch = 1.0;
utterance.volume = 1.0;
utterance.onend = () => {
setIsPlaying(false);
onPlayEnd?.();
};
utterance.onerror = () => {
setIsPlaying(false);
onError?.('語音播放失敗');
};
speechSynthesis.speak(utterance);
};
return (
<button
onClick={toggleTTS}
disabled={disabled}
title={isPlaying ? "點擊停止播放" : "點擊播放"}
aria-label={isPlaying ? `停止播放:${text}` : `播放:${text}`}
className={`group relative w-12 h-12 rounded-full shadow-lg transform transition-all duration-200
${isPlaying
? 'bg-gradient-to-br from-green-500 to-green-600 shadow-green-200 scale-105'
: 'bg-gradient-to-br from-blue-500 to-blue-600 hover:shadow-xl hover:scale-105 shadow-blue-200'
} ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} ${className}
`}
>
{/* 播放中波紋效果 */}
{isPlaying && (
<div className="absolute inset-0 bg-blue-400 rounded-full animate-ping opacity-40"></div>
)}
{/* 按鈕圖標 */}
<div className="relative z-10 flex items-center justify-center w-full h-full">
{isPlaying ? (
<svg className="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
</svg>
) : (
<svg className="w-6 h-6 text-white group-hover:scale-110 transition-transform" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z"/>
</svg>
)}
</div>
{/* 懸停提示光環 */}
{!disabled && (
<div className="absolute inset-0 rounded-full bg-white opacity-0 group-hover:opacity-20 transition-opacity duration-200"></div>
)}
</button>
);
}