Tốc độ và phát lại
Tốc độ
Nút tốc độ hiển thị tốc độ hiện tại, ví dụ 1x hoặc 1.5x. Mỗi lần chạm sẽ chuyển sang giá trị đầu tiên trong speeds lớn hơn tốc độ hiện tại, và quay về giá trị đầu tiên sau giá trị lớn nhất. Hãy liệt kê các tốc độ theo thứ tự tăng dần; nếu danh sách không được sắp xếp, nút có thể bỏ qua giá trị hoặc bị kẹt.
<AudioWaveformView
source={{ uri }}
speeds={[1, 1.25, 1.5, 2]}
defaultSpeed={1}
style={{ height: 56 }}
/>defaultSpeedlà tốc độ ban đầu của nút. Nó chỉ được áp dụng một lần: sau khi tốc độ đầu tiên được áp dụng (bởi chínhdefaultSpeed, một lần chạm hoặcsetSpeed()), các thay đổi sau đó củadefaultSpeedđều bị bỏ qua. Muốn đổi tốc độ về sau, hãy gọisetSpeed()hoặc dùng prop controlledspeed.- Khoảng giá trị. Native player giới hạn tốc độ trong khoảng 0.25 đến 4. Nút tốc độ và các event vẫn hiển thị giá trị bạn đặt, nên hãy giữ trong khoảng đó.
- Nhãn hiển thị một chữ số thập phân. iOS làm tròn (
0.75hiển thị0.8x) còn Android cắt bớt (0.75hiển thị0.7x), nên hãy ưu tiên tốc độ có tối đa một chữ số thập phân. Xem Lưu ý theo nền tảng.
Tự động phát và vị trí bắt đầu
<AudioWaveformView
source={{ uri }}
autoPlay
initialPositionMs={12_000}
style={{ height: 56 }}
/>Cả hai được áp dụng khi nguồn sẵn sàng (ngay sau onLoad), một lần cho mỗi nguồn. autoPlay bị bỏ qua khi prop playing được đặt; khi đó hãy dùng playing={true}.
Phát lặp
Với loop, khi đến cuối, việc phát quay về 0 và tiếp tục. onEnd không được gọi và trạng thái vẫn là ready.
Chạm play khi đang tải
Bạn không cần chờ onLoad. Một lần chạm nút play, một lời gọi play() hay playing={true} trong lúc đang tải đều được ghi nhớ, và việc phát bắt đầu ngay khi nguồn sẵn sàng. Spinner vẫn hiển thị cho đến lúc đó.
Kéo để tua
Nhấn vào bất kỳ đâu trên waveform, playhead sẽ nhảy đến đó ngay lập tức. Kéo để di chuyển nó.
- Khi ngón tay còn chạm, việc phát tạm dừng và
onTimeUpdatengừng được gọi. - Khi thả tay, player tua đến vị trí cuối cùng,
onSeekđược gọi, và việc phát tiếp tục nếu trước đó đang phát. Việc phát không tiếp tục nếu hệ thống đã hủy thao tác chạm, hoặc nếuplayingđang được controlled với giá trịfalse. - Waveform giành lấy thao tác chạm ngay khi bắt đầu, nên
ScrollViewhoặcFlatListcha không thể chiếm thao tác kéo ngang. Một cú vuốt dọc bắt đầu trên waveform sẽ tua thay vì cuộn danh sách. - Trước khi nguồn sẵn sàng, thời lượng chưa được biết nên kéo không thể tua.
Khi phát hết
Khi âm thanh phát đến cuối mà loop tắt, playhead giữ ở trạng thái đầy, nút hiển thị biểu tượng play, trạng thái là ended và onEnd được gọi. Lần phát tiếp theo bắt đầu từ 0, kể cả khi bạn tua ở giữa. Muốn bắt đầu từ vị trí khác, hãy phát trước rồi mới tua.
Controlled mode
Mặc định, component tự quản lý trạng thái phát và tốc độ. Đặt playing và/hoặc speed để quản lý chúng từ state của bạn:
- Chạm vào nút play hoặc nút tốc độ không làm thay đổi việc phát. Nó gửi
onPlayerStateChangevới giá trị được yêu cầu. - Bạn quyết định có áp dụng hay không bằng cách cập nhật prop.
play(),pause()vàtoggle()không làm gì khiplayingđược đặt, vàsetSpeed()không làm gì khispeedđược đặt.seekTo()vẫn hoạt động.
const [playing, setPlaying] = useState(false);
const [speed, setSpeed] = useState(1);
<AudioWaveformView
source={{ uri }}
playing={playing}
speed={speed}
onPlayerStateChange={(e) => {
// Khi đang tải, snapshot báo isPlaying: false kể cả khi đã có yêu cầu phát đang chờ.
if (e.isPlaying || (e.state !== 'loading' && e.state !== 'idle')) {
setPlaying(e.isPlaying);
}
setSpeed(e.speed);
}}
style={{ height: 56 }}
/>;Việc kiểm tra state rất quan trọng. Đặt playing={true} khi nguồn còn đang tải sẽ xếp lệnh phát vào hàng chờ, nhưng snapshot ngay sau đó vẫn báo isPlaying: false. Nếu chép giá trị đó ngược lại, playing sẽ thành false và lệnh phát đang chờ bị hủy.
Mỗi lần một tin nhắn thoại
Mỗi AudioWaveformView có native player riêng, nên nhiều view có thể phát cùng lúc. Trong app chat, bạn thường muốn khi bắt đầu phát một tin nhắn thì các tin khác dừng lại. Hãy lưu id của tin đang phát trong state và điều khiển playing:
function VoiceNoteList({ notes }: { notes: { id: string; uri: string }[] }) {
const [activeId, setActiveId] = useState<string | null>(null);
return (
<FlatList
data={notes}
extraData={activeId}
keyExtractor={(note) => note.id}
renderItem={({ item }) => (
<AudioWaveformView
source={{ uri: item.uri }}
playing={activeId === item.id}
onPlayerStateChange={(e) => {
if (e.isPlaying) {
setActiveId(item.id);
} else if (e.state !== 'loading' && e.state !== 'idle') {
setActiveId((current) => (current === item.id ? null : current));
}
}}
style={{ height: 56, marginVertical: 4 }}
/>
)}
/>
);
}Chạm play trên một tin nhắn sẽ đặt nó thành tin đang phát và dừng tin trước đó. Tạm dừng nó, hoặc phát đến cuối, sẽ xóa activeId.
Samples tính sẵn
Mặc định, component giải mã âm thanh ngay trên thiết bị để vẽ waveform. Với file remote, điều này nghĩa là phải tải thêm một lần nữa bên cạnh lần stream của chính player (xem Lưu ý theo nền tảng). Nếu backend của bạn đã lưu sẵn dữ liệu đỉnh sóng, hãy truyền chúng qua samples để bỏ qua bước giải mã:
<AudioWaveformView
source={{ uri: message.audioUrl }}
samples={message.peaks} // ví dụ 64 giá trị từ 0 đến 1
style={{ height: 56 }}
/>- Giá trị nên nằm trong
[0, 1]. Nếu có giá trị lớn hơn1, cả mảng sẽ được chia cho giá trị lớn nhất. - Mảng được lấy mẫu lại theo số thanh, nên 50 đến 100 giá trị là quá đủ cho một bong bóng chat.
- Giá trị đúng bằng
0được vẽ ở chiều cao placeholder. Hãy dùng một số dương nhỏ cho đoạn im lặng. - Waveform hiển thị ngay lập tức, không có giai đoạn placeholder.
Đổi nguồn
Đổi source.uri trên một component đang mount sẽ dừng phát và tải file mới; autoPlay và initialPositionMs được áp dụng lại. Trên iOS, tốc độ được giữ nguyên. Trên Android, tốc độ và waveform cũ hoạt động khác, xem Lưu ý theo nền tảng. Nếu bạn muốn mỗi file bắt đầu từ trạng thái sạch, hãy đặt key={uri} cho component để React mount một native view mới cho mỗi nguồn.