@wavv/ui 2.4.14 → 2.4.15-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/Audio.js +49 -2
- package/package.json +1 -1
|
@@ -13,6 +13,9 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
13
13
|
const [displayCurrent, setDisplayCurrent] = useState('');
|
|
14
14
|
const [displayDuration, setDisplayDuration] = useState('0:00');
|
|
15
15
|
const audioRef = useRef(null);
|
|
16
|
+
const isSeekingRef = useRef(false);
|
|
17
|
+
const seekedTimeoutRef = useRef(null);
|
|
18
|
+
const END_EPSILON_SECONDS = 0.75;
|
|
16
19
|
const { height = 30, showHoverTime = false, playedColor, unplayedColor } = 'object' == typeof waveform ? waveform : {};
|
|
17
20
|
const play = ()=>{
|
|
18
21
|
const { current: audio } = audioRef;
|
|
@@ -32,9 +35,20 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
32
35
|
const { current: audio } = audioRef;
|
|
33
36
|
return audio ? !audio.paused : false;
|
|
34
37
|
};
|
|
38
|
+
const isNearEnd = (sec)=>totalDuration > 0 && totalDuration - sec <= END_EPSILON_SECONDS;
|
|
35
39
|
const togglePlayPause = ()=>{
|
|
36
40
|
if (isPlaying) pause();
|
|
37
|
-
else
|
|
41
|
+
else {
|
|
42
|
+
if (isNearEnd(progress)) {
|
|
43
|
+
setProgress(0);
|
|
44
|
+
setDisplayCurrent('0:00');
|
|
45
|
+
const { current: audio } = audioRef;
|
|
46
|
+
if (audio) try {
|
|
47
|
+
audio.currentTime = 0;
|
|
48
|
+
} catch (_e) {}
|
|
49
|
+
}
|
|
50
|
+
play();
|
|
51
|
+
}
|
|
38
52
|
};
|
|
39
53
|
useImperativeHandle(ref, ()=>({
|
|
40
54
|
play,
|
|
@@ -56,6 +70,16 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
56
70
|
const handleAudioStop = (event)=>{
|
|
57
71
|
setIsPlaying(false);
|
|
58
72
|
setIsCleared(true);
|
|
73
|
+
setProgress(0);
|
|
74
|
+
setDisplayCurrent('0:00');
|
|
75
|
+
const { current: audio } = audioRef;
|
|
76
|
+
if (audio) try {
|
|
77
|
+
audio.currentTime = 0;
|
|
78
|
+
window.setTimeout(()=>{
|
|
79
|
+
const ct = audio.currentTime ?? 0;
|
|
80
|
+
if (ct > 0.5) audio.currentTime = 0;
|
|
81
|
+
}, 120);
|
|
82
|
+
} catch (_e) {}
|
|
59
83
|
if (onStop) onStop(event);
|
|
60
84
|
};
|
|
61
85
|
const handleAudioError = ()=>{
|
|
@@ -88,6 +112,9 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
88
112
|
src,
|
|
89
113
|
fallbackDuration
|
|
90
114
|
]);
|
|
115
|
+
useEffect(()=>()=>{
|
|
116
|
+
if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
|
|
117
|
+
}, []);
|
|
91
118
|
const formatTime = (time)=>{
|
|
92
119
|
if (!Number.isFinite(time) || time < 0) return '0:00';
|
|
93
120
|
const mins = Math.floor(time / 60);
|
|
@@ -95,6 +122,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
95
122
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
96
123
|
};
|
|
97
124
|
const updateProgress = ()=>{
|
|
125
|
+
if (isSeekingRef.current) return;
|
|
98
126
|
const { current: audio } = audioRef;
|
|
99
127
|
if (audio) {
|
|
100
128
|
const { currentTime } = audio;
|
|
@@ -128,9 +156,27 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
128
156
|
}, 100);
|
|
129
157
|
}
|
|
130
158
|
};
|
|
159
|
+
const handleSeeked = ()=>{
|
|
160
|
+
isSeekingRef.current = false;
|
|
161
|
+
if (seekedTimeoutRef.current) {
|
|
162
|
+
clearTimeout(seekedTimeoutRef.current);
|
|
163
|
+
seekedTimeoutRef.current = null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
131
166
|
const handleControlGrab = (value)=>{
|
|
132
167
|
const sliderPosition = Array.isArray(value) ? value[0] : value;
|
|
133
|
-
if (audioRef?.current
|
|
168
|
+
if (!audioRef?.current || !Number.isFinite(sliderPosition) || !Number.isFinite(totalDuration) || totalDuration <= 0) return;
|
|
169
|
+
const maxSeekTarget = Math.max(0, totalDuration - END_EPSILON_SECONDS);
|
|
170
|
+
const clamped = Math.min(Math.max(sliderPosition, 0), maxSeekTarget);
|
|
171
|
+
isSeekingRef.current = true;
|
|
172
|
+
if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
|
|
173
|
+
seekedTimeoutRef.current = setTimeout(()=>{
|
|
174
|
+
isSeekingRef.current = false;
|
|
175
|
+
seekedTimeoutRef.current = null;
|
|
176
|
+
}, 300);
|
|
177
|
+
setProgress(clamped);
|
|
178
|
+
setDisplayCurrent(formatTime(clamped));
|
|
179
|
+
audioRef.current.currentTime = clamped;
|
|
134
180
|
};
|
|
135
181
|
const hideSlider = isCleared && hideSliderOnStop;
|
|
136
182
|
const hideTime = isCleared && hideTimeOnStop;
|
|
@@ -199,6 +245,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
|
|
|
199
245
|
onEmptied: handleAudioStop,
|
|
200
246
|
onEnded: handleAudioStop,
|
|
201
247
|
onTimeUpdate: updateProgress,
|
|
248
|
+
onSeeked: handleSeeked,
|
|
202
249
|
onDurationChange: handleLoad,
|
|
203
250
|
onLoadedMetadata: handleLoad,
|
|
204
251
|
onLoadedData: handleLoad,
|