@douyinfe/semi-foundation 2.79.0 → 2.80.0
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/cascader/foundation.ts +2 -2
- package/lib/cjs/cascader/foundation.js +1 -1
- package/lib/cjs/videoPlayer/animation.scss +7 -0
- package/lib/cjs/videoPlayer/constants.d.ts +30 -0
- package/lib/cjs/videoPlayer/constants.js +48 -0
- package/lib/cjs/videoPlayer/foundation.d.ts +75 -0
- package/lib/cjs/videoPlayer/foundation.js +291 -0
- package/lib/cjs/videoPlayer/progressFoundation.d.ts +38 -0
- package/lib/cjs/videoPlayer/progressFoundation.js +131 -0
- package/lib/cjs/videoPlayer/variables.scss +75 -0
- package/lib/cjs/videoPlayer/videoPlayer.css +279 -0
- package/lib/cjs/videoPlayer/videoPlayer.scss +323 -0
- package/lib/es/cascader/foundation.js +1 -1
- package/lib/es/videoPlayer/animation.scss +7 -0
- package/lib/es/videoPlayer/constants.d.ts +30 -0
- package/lib/es/videoPlayer/constants.js +43 -0
- package/lib/es/videoPlayer/foundation.d.ts +75 -0
- package/lib/es/videoPlayer/foundation.js +283 -0
- package/lib/es/videoPlayer/progressFoundation.d.ts +38 -0
- package/lib/es/videoPlayer/progressFoundation.js +123 -0
- package/lib/es/videoPlayer/variables.scss +75 -0
- package/lib/es/videoPlayer/videoPlayer.css +279 -0
- package/lib/es/videoPlayer/videoPlayer.scss +323 -0
- package/package.json +4 -4
- package/videoPlayer/animation.scss +7 -0
- package/videoPlayer/constants.ts +39 -0
- package/videoPlayer/foundation.ts +332 -0
- package/videoPlayer/progressFoundation.ts +136 -0
- package/videoPlayer/variables.scss +75 -0
- package/videoPlayer/videoPlayer.scss +323 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
declare const cssClasses: {
|
|
2
|
+
readonly PREFIX: "semi-videoPlayer";
|
|
3
|
+
readonly PREFIX_CONTROLS: "semi-videoPlayer-controls";
|
|
4
|
+
readonly PREFIX_PROGRESS: "semi-videoPlayer-progress";
|
|
5
|
+
};
|
|
6
|
+
declare const strings: {
|
|
7
|
+
readonly DARK: "dark";
|
|
8
|
+
readonly LIGHT: "light";
|
|
9
|
+
readonly PLAY: "play";
|
|
10
|
+
readonly NEXT: "next";
|
|
11
|
+
readonly TIME: "time";
|
|
12
|
+
readonly VOLUME: "volume";
|
|
13
|
+
readonly PLAYBACK_RATE: "playbackRate";
|
|
14
|
+
readonly QUALITY: "quality";
|
|
15
|
+
readonly ROUTE: "route";
|
|
16
|
+
readonly MIRROR: "mirror";
|
|
17
|
+
readonly FULLSCREEN: "fullscreen";
|
|
18
|
+
readonly PICTURE_IN_PICTURE: "pictureInPicture";
|
|
19
|
+
};
|
|
20
|
+
declare const numbers: {
|
|
21
|
+
readonly DEFAULT_VOLUME: 100;
|
|
22
|
+
readonly DEFAULT_SEEK_TIME: 10;
|
|
23
|
+
readonly DEFAULT_VOLUME_STEP: 10;
|
|
24
|
+
readonly DEFAULT_PLAYBACK_RATE: 1;
|
|
25
|
+
};
|
|
26
|
+
declare const DEFAULT_PLAYBACK_RATE: {
|
|
27
|
+
label: string;
|
|
28
|
+
value: number;
|
|
29
|
+
}[];
|
|
30
|
+
export { cssClasses, strings, numbers, DEFAULT_PLAYBACK_RATE };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BASE_CLASS_PREFIX } from '../base/constants';
|
|
2
|
+
const cssClasses = {
|
|
3
|
+
PREFIX: `${BASE_CLASS_PREFIX}-videoPlayer`,
|
|
4
|
+
PREFIX_CONTROLS: `${BASE_CLASS_PREFIX}-videoPlayer-controls`,
|
|
5
|
+
PREFIX_PROGRESS: `${BASE_CLASS_PREFIX}-videoPlayer-progress`
|
|
6
|
+
};
|
|
7
|
+
const strings = {
|
|
8
|
+
DARK: 'dark',
|
|
9
|
+
LIGHT: 'light',
|
|
10
|
+
PLAY: 'play',
|
|
11
|
+
NEXT: 'next',
|
|
12
|
+
TIME: 'time',
|
|
13
|
+
VOLUME: 'volume',
|
|
14
|
+
PLAYBACK_RATE: 'playbackRate',
|
|
15
|
+
QUALITY: 'quality',
|
|
16
|
+
ROUTE: 'route',
|
|
17
|
+
MIRROR: 'mirror',
|
|
18
|
+
FULLSCREEN: 'fullscreen',
|
|
19
|
+
PICTURE_IN_PICTURE: 'pictureInPicture'
|
|
20
|
+
};
|
|
21
|
+
const numbers = {
|
|
22
|
+
DEFAULT_VOLUME: 100,
|
|
23
|
+
DEFAULT_SEEK_TIME: 10,
|
|
24
|
+
DEFAULT_VOLUME_STEP: 10,
|
|
25
|
+
DEFAULT_PLAYBACK_RATE: 1
|
|
26
|
+
};
|
|
27
|
+
const DEFAULT_PLAYBACK_RATE = [{
|
|
28
|
+
label: '2.0x',
|
|
29
|
+
value: 2
|
|
30
|
+
}, {
|
|
31
|
+
label: '1.5x',
|
|
32
|
+
value: 1.5
|
|
33
|
+
}, {
|
|
34
|
+
label: '1.25x',
|
|
35
|
+
value: 1.25
|
|
36
|
+
}, {
|
|
37
|
+
label: '1.0x',
|
|
38
|
+
value: 1
|
|
39
|
+
}, {
|
|
40
|
+
label: '0.75x',
|
|
41
|
+
value: 0.75
|
|
42
|
+
}];
|
|
43
|
+
export { cssClasses, strings, numbers, DEFAULT_PLAYBACK_RATE };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference types="lodash" />
|
|
2
|
+
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
3
|
+
export interface VideoPlayerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
4
|
+
getVideo: () => HTMLVideoElement | null;
|
|
5
|
+
getVideoWrapper: () => HTMLDivElement | null;
|
|
6
|
+
notifyPause: () => void;
|
|
7
|
+
notifyPlay: () => void;
|
|
8
|
+
notifyQualityChange: (quality: string) => void;
|
|
9
|
+
notifyRateChange: (rate: number) => void;
|
|
10
|
+
notifyRouteChange: (route: string) => void;
|
|
11
|
+
notifyVolumeChange: (volume: number) => void;
|
|
12
|
+
setBufferedValue: (bufferedValue: number) => void;
|
|
13
|
+
setCurrentTime: (currentTime: number) => void;
|
|
14
|
+
setIsError: (isError: boolean) => void;
|
|
15
|
+
setIsMirror: (isMirror: boolean) => void;
|
|
16
|
+
setIsPlaying: (isPlaying: boolean) => void;
|
|
17
|
+
setMuted: (muted: boolean) => void;
|
|
18
|
+
setNotificationContent: (content: string) => void;
|
|
19
|
+
setPlaybackRate: (rate: number) => void;
|
|
20
|
+
setQuality: (quality: string) => void;
|
|
21
|
+
setRoute: (route: string) => void;
|
|
22
|
+
setShowControls: (showControls: boolean) => void;
|
|
23
|
+
setShowNotification: (showNotification: boolean) => void;
|
|
24
|
+
setTotalTime: (totalTime: number) => void;
|
|
25
|
+
setVolume: (volume: number) => void;
|
|
26
|
+
}
|
|
27
|
+
export default class VideoPlayerFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<VideoPlayerAdapter<P, S>, P, S> {
|
|
28
|
+
constructor(adapter: VideoPlayerAdapter<P, S>);
|
|
29
|
+
private controlsTimer;
|
|
30
|
+
private scrollPosition;
|
|
31
|
+
init(): void;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
shouldShowControlItem(name: string): boolean;
|
|
34
|
+
clearTimer(): void;
|
|
35
|
+
handleMouseMove: import("lodash").DebouncedFuncLeading<() => void>;
|
|
36
|
+
handleTimeChange(value: number): void;
|
|
37
|
+
handleTimeUpdate(): void;
|
|
38
|
+
handleDurationChange(): void;
|
|
39
|
+
handleError(): void;
|
|
40
|
+
handlePlayOrPause(): void;
|
|
41
|
+
handlePlay(): void;
|
|
42
|
+
handlePause(): void;
|
|
43
|
+
handleCanPlay: () => void;
|
|
44
|
+
handleWaiting: (locale: any) => void;
|
|
45
|
+
handleStalled: (locale: any) => void;
|
|
46
|
+
handleProgress: () => void;
|
|
47
|
+
handleEnded: () => void;
|
|
48
|
+
handleVolumeChange(value: number): void;
|
|
49
|
+
handleVolumeSilent: () => void;
|
|
50
|
+
checkFullScreen(): boolean;
|
|
51
|
+
handleFullscreen: () => void;
|
|
52
|
+
handleRateChange(rate: {
|
|
53
|
+
label: string;
|
|
54
|
+
value: number;
|
|
55
|
+
}, locale: any): void;
|
|
56
|
+
handleQualityChange(quality: {
|
|
57
|
+
label: string;
|
|
58
|
+
value: string;
|
|
59
|
+
}, locale: any): void;
|
|
60
|
+
handleRouteChange(route: {
|
|
61
|
+
label: string;
|
|
62
|
+
value: string;
|
|
63
|
+
}, locale: any): void;
|
|
64
|
+
handleMirror: (locale: any) => void;
|
|
65
|
+
handlePictureInPicture: () => void;
|
|
66
|
+
handleLeavePictureInPicture: () => void;
|
|
67
|
+
handleTemporaryNotification: (content: string) => void;
|
|
68
|
+
restorePlayPosition(): void;
|
|
69
|
+
handleMouseEnterWrapper: () => void;
|
|
70
|
+
handleMouseLeaveWrapper: () => void;
|
|
71
|
+
handleFullscreenChange: () => void;
|
|
72
|
+
registerEvent: () => void;
|
|
73
|
+
unregisterEvent: () => void;
|
|
74
|
+
handleBodyKeyDown(e: KeyboardEvent): void;
|
|
75
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import _throttle from "lodash/throttle";
|
|
2
|
+
import BaseFoundation from '../base/foundation';
|
|
3
|
+
export default class VideoPlayerFoundation extends BaseFoundation {
|
|
4
|
+
constructor(adapter) {
|
|
5
|
+
super(Object.assign({}, adapter));
|
|
6
|
+
this.scrollPosition = null;
|
|
7
|
+
this.handleMouseMove = _throttle(() => {
|
|
8
|
+
this._adapter.setShowControls(true);
|
|
9
|
+
this.clearTimer();
|
|
10
|
+
this.controlsTimer = setTimeout(() => {
|
|
11
|
+
this._adapter.setShowControls(false);
|
|
12
|
+
}, 3000);
|
|
13
|
+
}, 200);
|
|
14
|
+
this.handleCanPlay = () => {
|
|
15
|
+
this._adapter.setShowNotification(false);
|
|
16
|
+
};
|
|
17
|
+
this.handleWaiting = locale => {
|
|
18
|
+
this._adapter.setNotificationContent(locale.loading);
|
|
19
|
+
this._adapter.setShowNotification(true);
|
|
20
|
+
};
|
|
21
|
+
this.handleStalled = locale => {
|
|
22
|
+
this._adapter.setNotificationContent(locale.stall);
|
|
23
|
+
this._adapter.setShowNotification(true);
|
|
24
|
+
};
|
|
25
|
+
this.handleProgress = () => {
|
|
26
|
+
const video = this._adapter.getVideo();
|
|
27
|
+
if (video && video.buffered.length > 0) {
|
|
28
|
+
const bufferedEnd = video.buffered.end(video.buffered.length - 1);
|
|
29
|
+
this._adapter.setBufferedValue(bufferedEnd);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
this.handleEnded = () => {
|
|
33
|
+
this._adapter.setIsPlaying(false);
|
|
34
|
+
this._adapter.setShowControls(true);
|
|
35
|
+
};
|
|
36
|
+
this.handleVolumeSilent = () => {
|
|
37
|
+
const video = this._adapter.getVideo();
|
|
38
|
+
const {
|
|
39
|
+
volume,
|
|
40
|
+
muted
|
|
41
|
+
} = this.getStates();
|
|
42
|
+
if (!video) return;
|
|
43
|
+
if (muted) {
|
|
44
|
+
video.volume = volume / 100;
|
|
45
|
+
this._adapter.setVolume(volume);
|
|
46
|
+
this._adapter.setMuted(false);
|
|
47
|
+
} else {
|
|
48
|
+
video.volume = 0;
|
|
49
|
+
this._adapter.setMuted(true);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
this.handleFullscreen = () => {
|
|
53
|
+
const videoWrapper = this._adapter.getVideoWrapper();
|
|
54
|
+
const isFullScreen = this.checkFullScreen();
|
|
55
|
+
if (videoWrapper) {
|
|
56
|
+
if (isFullScreen) {
|
|
57
|
+
document.exitFullscreen();
|
|
58
|
+
} else {
|
|
59
|
+
// record scroll position before entering fullscreen
|
|
60
|
+
this.scrollPosition = {
|
|
61
|
+
x: window.scrollX,
|
|
62
|
+
y: window.scrollY
|
|
63
|
+
};
|
|
64
|
+
videoWrapper.requestFullscreen();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
this.handleMirror = locale => {
|
|
69
|
+
const {
|
|
70
|
+
isMirror
|
|
71
|
+
} = this.getStates();
|
|
72
|
+
this._adapter.setIsMirror(!isMirror);
|
|
73
|
+
this.handleTemporaryNotification(!isMirror ? locale.mirror : locale.cancelMirror);
|
|
74
|
+
};
|
|
75
|
+
this.handlePictureInPicture = () => {
|
|
76
|
+
const video = this._adapter.getVideo();
|
|
77
|
+
if (!video) return;
|
|
78
|
+
video.requestPictureInPicture();
|
|
79
|
+
};
|
|
80
|
+
this.handleLeavePictureInPicture = () => {
|
|
81
|
+
const video = this._adapter.getVideo();
|
|
82
|
+
if (!video) return;
|
|
83
|
+
this._adapter.setIsPlaying(!video.paused);
|
|
84
|
+
};
|
|
85
|
+
this.handleTemporaryNotification = content => {
|
|
86
|
+
this._adapter.setNotificationContent(content);
|
|
87
|
+
this._adapter.setShowNotification(true);
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
this._adapter.setShowNotification(false);
|
|
90
|
+
}, 1000);
|
|
91
|
+
};
|
|
92
|
+
this.handleMouseEnterWrapper = () => {
|
|
93
|
+
this._adapter.setShowControls(true);
|
|
94
|
+
};
|
|
95
|
+
this.handleMouseLeaveWrapper = () => {
|
|
96
|
+
const {
|
|
97
|
+
isPlaying
|
|
98
|
+
} = this.getStates();
|
|
99
|
+
if (isPlaying) {
|
|
100
|
+
this._adapter.setShowControls(false);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
this.handleFullscreenChange = () => {
|
|
104
|
+
const isFullScreen = this.checkFullScreen();
|
|
105
|
+
if (isFullScreen) {
|
|
106
|
+
document.addEventListener('mousemove', this.handleMouseMove);
|
|
107
|
+
} else {
|
|
108
|
+
// according to the exit fullScreen has two way, Esc && click the button
|
|
109
|
+
// so we need to restore scroll position after exiting fullscreen
|
|
110
|
+
if (this.scrollPosition) {
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
|
|
113
|
+
this.scrollPosition = null;
|
|
114
|
+
}, 0);
|
|
115
|
+
}
|
|
116
|
+
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
this.registerEvent = () => {
|
|
120
|
+
const video = this._adapter.getVideo();
|
|
121
|
+
if (!video) return;
|
|
122
|
+
document.addEventListener('keydown', e => this.handleBodyKeyDown(e));
|
|
123
|
+
document.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
|
124
|
+
video.addEventListener('leavepictureinpicture', this.handleLeavePictureInPicture);
|
|
125
|
+
};
|
|
126
|
+
this.unregisterEvent = () => {
|
|
127
|
+
const video = this._adapter.getVideo();
|
|
128
|
+
if (!video) return;
|
|
129
|
+
document.removeEventListener('keydown', e => this.handleBodyKeyDown(e));
|
|
130
|
+
document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
|
131
|
+
video.removeEventListener('leavepictureinpicture', this.handleLeavePictureInPicture);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
init() {
|
|
135
|
+
const {
|
|
136
|
+
volume,
|
|
137
|
+
muted
|
|
138
|
+
} = this.getProps();
|
|
139
|
+
const video = this._adapter.getVideo();
|
|
140
|
+
if (video) {
|
|
141
|
+
this._adapter.setTotalTime(video.duration);
|
|
142
|
+
this.handleVolumeChange(muted ? 0 : volume);
|
|
143
|
+
}
|
|
144
|
+
this.registerEvent();
|
|
145
|
+
}
|
|
146
|
+
destroy() {
|
|
147
|
+
this.unregisterEvent();
|
|
148
|
+
this.clearTimer();
|
|
149
|
+
}
|
|
150
|
+
shouldShowControlItem(name) {
|
|
151
|
+
const {
|
|
152
|
+
controlsList
|
|
153
|
+
} = this.getProps();
|
|
154
|
+
if (controlsList.includes(name)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
clearTimer() {
|
|
160
|
+
if (this.controlsTimer) {
|
|
161
|
+
clearTimeout(this.controlsTimer);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
handleTimeChange(value) {
|
|
165
|
+
const video = this._adapter.getVideo();
|
|
166
|
+
if (!video) return;
|
|
167
|
+
if (!Number.isNaN(value)) {
|
|
168
|
+
video.currentTime = value;
|
|
169
|
+
this._adapter.setCurrentTime(value);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
handleTimeUpdate() {
|
|
173
|
+
const video = this._adapter.getVideo();
|
|
174
|
+
if (!video) return;
|
|
175
|
+
this._adapter.setCurrentTime(video.currentTime);
|
|
176
|
+
}
|
|
177
|
+
handleDurationChange() {
|
|
178
|
+
const video = this._adapter.getVideo();
|
|
179
|
+
if (!video) return;
|
|
180
|
+
this._adapter.setTotalTime(video.duration);
|
|
181
|
+
}
|
|
182
|
+
handleError() {
|
|
183
|
+
this._adapter.setIsError(true);
|
|
184
|
+
}
|
|
185
|
+
handlePlayOrPause() {
|
|
186
|
+
const video = this._adapter.getVideo();
|
|
187
|
+
if (!video) return;
|
|
188
|
+
video.paused ? this.handlePlay() : this.handlePause();
|
|
189
|
+
}
|
|
190
|
+
handlePlay() {
|
|
191
|
+
const video = this._adapter.getVideo();
|
|
192
|
+
if (video) {
|
|
193
|
+
video.play();
|
|
194
|
+
this._adapter.setIsPlaying(true);
|
|
195
|
+
this._adapter.notifyPlay();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
handlePause() {
|
|
199
|
+
const video = this._adapter.getVideo();
|
|
200
|
+
if (video) {
|
|
201
|
+
video.pause();
|
|
202
|
+
this._adapter.setIsPlaying(false);
|
|
203
|
+
this._adapter.notifyPause();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
handleVolumeChange(value) {
|
|
207
|
+
const video = this._adapter.getVideo();
|
|
208
|
+
if (!video) return;
|
|
209
|
+
const volume = Math.floor(value > 0 ? value : 0);
|
|
210
|
+
video.volume = volume / 100;
|
|
211
|
+
this._adapter.setVolume(volume);
|
|
212
|
+
this._adapter.setMuted(volume === 0 ? true : false);
|
|
213
|
+
}
|
|
214
|
+
checkFullScreen() {
|
|
215
|
+
const videoWrapper = this._adapter.getVideoWrapper();
|
|
216
|
+
if (!videoWrapper) return false;
|
|
217
|
+
return !!(document.fullscreenElement === videoWrapper ||
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
(document === null || document === void 0 ? void 0 : document.webkitFullscreenElement) === videoWrapper ||
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
(document === null || document === void 0 ? void 0 : document.mozFullScreenElement) === videoWrapper ||
|
|
222
|
+
// @ts-ignore
|
|
223
|
+
(document === null || document === void 0 ? void 0 : document.msFullscreenElement) === videoWrapper || (
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
videoWrapper === null || videoWrapper === void 0 ? void 0 : videoWrapper.webkitDisplayingFullscreen) // iOS Safari 特殊处理
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
handleRateChange(rate, locale) {
|
|
229
|
+
const video = this._adapter.getVideo();
|
|
230
|
+
if (!video) return;
|
|
231
|
+
video.playbackRate = rate.value;
|
|
232
|
+
this._adapter.setPlaybackRate(rate.value);
|
|
233
|
+
this._adapter.notifyRateChange(rate.value);
|
|
234
|
+
this.handleTemporaryNotification(locale.rateChange.replace('${rate}', rate.label));
|
|
235
|
+
}
|
|
236
|
+
handleQualityChange(quality, locale) {
|
|
237
|
+
this._adapter.setQuality(quality.value);
|
|
238
|
+
this._adapter.notifyQualityChange(quality.value);
|
|
239
|
+
this.handleTemporaryNotification(locale.qualityChange.replace('${quality}', quality.label));
|
|
240
|
+
this.restorePlayPosition();
|
|
241
|
+
}
|
|
242
|
+
handleRouteChange(route, locale) {
|
|
243
|
+
var _a, _b;
|
|
244
|
+
this._adapter.setRoute(route.value);
|
|
245
|
+
(_b = (_a = this._adapter).notifyRouteChange) === null || _b === void 0 ? void 0 : _b.call(_a, route.value);
|
|
246
|
+
this.handleTemporaryNotification(locale.routeChange.replace('${route}', route.label));
|
|
247
|
+
this.restorePlayPosition();
|
|
248
|
+
}
|
|
249
|
+
restorePlayPosition() {
|
|
250
|
+
const video = this._adapter.getVideo();
|
|
251
|
+
if (!video) return;
|
|
252
|
+
const wasPlaying = !video.paused;
|
|
253
|
+
const currentTime = video.currentTime;
|
|
254
|
+
const handleLoaded = () => {
|
|
255
|
+
video.currentTime = currentTime;
|
|
256
|
+
if (wasPlaying) {
|
|
257
|
+
video.play();
|
|
258
|
+
}
|
|
259
|
+
video.removeEventListener('loadeddata', handleLoaded);
|
|
260
|
+
};
|
|
261
|
+
video.addEventListener('loadeddata', handleLoaded);
|
|
262
|
+
}
|
|
263
|
+
handleBodyKeyDown(e) {
|
|
264
|
+
const {
|
|
265
|
+
currentTime,
|
|
266
|
+
volume
|
|
267
|
+
} = this.getStates();
|
|
268
|
+
const {
|
|
269
|
+
seekTime
|
|
270
|
+
} = this.getProps();
|
|
271
|
+
if (e.key === ' ') {
|
|
272
|
+
this.handlePlayOrPause();
|
|
273
|
+
// } else if (e.key === 'ArrowUp') {
|
|
274
|
+
// this.handleVolumeChange(volume + numbers.DEFAULT_VOLUME_STEP);
|
|
275
|
+
// } else if (e.key === 'ArrowDown') {
|
|
276
|
+
// this.handleVolumeChange(volume - numbers.DEFAULT_VOLUME_STEP);
|
|
277
|
+
} else if (e.key === 'ArrowLeft') {
|
|
278
|
+
this.handleTimeChange(currentTime - seekTime);
|
|
279
|
+
} else if (e.key === 'ArrowRight') {
|
|
280
|
+
this.handleTimeChange(currentTime + seekTime);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import BaseFoundation, { DefaultAdapter } from '../base/foundation';
|
|
2
|
+
export interface MarkerListItem {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
title: string;
|
|
6
|
+
width: string;
|
|
7
|
+
left: string;
|
|
8
|
+
}
|
|
9
|
+
export interface Marker {
|
|
10
|
+
start: number;
|
|
11
|
+
title: string;
|
|
12
|
+
}
|
|
13
|
+
export interface VideoProgressAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
14
|
+
getSliderRef: () => HTMLDivElement | null;
|
|
15
|
+
getMarkersList: () => MarkerListItem[];
|
|
16
|
+
setIsDragging: (isDragging: boolean) => void;
|
|
17
|
+
setIsHandleHovering: (isHandleHovering: boolean) => void;
|
|
18
|
+
setActiveIndex: (activeIndex: number) => void;
|
|
19
|
+
setMovingInfo: (movingInfo: {
|
|
20
|
+
progress: number;
|
|
21
|
+
offset: number;
|
|
22
|
+
value: number;
|
|
23
|
+
} | null) => void;
|
|
24
|
+
}
|
|
25
|
+
export default class VideoProgressFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<VideoProgressAdapter<P, S>, P, S> {
|
|
26
|
+
constructor(adapter: VideoProgressAdapter<P, S>);
|
|
27
|
+
handleDocumentMouseMove: (e: MouseEvent) => void;
|
|
28
|
+
handleDocumentMouseUp: () => void;
|
|
29
|
+
handleMouseDown: (e: any) => void;
|
|
30
|
+
handleMouseUp: () => void;
|
|
31
|
+
handleMouseEvent: (e: any, shouldSetValue?: boolean) => void;
|
|
32
|
+
handleSliderMouseEnter: (index: number) => void;
|
|
33
|
+
handleSliderMouseLeave: (index: number) => void;
|
|
34
|
+
setActiveIndex: (currentValue: number) => void;
|
|
35
|
+
getValueWidth: (marker: MarkerListItem, value: number) => string;
|
|
36
|
+
getPlayedWidth: (marker: MarkerListItem) => string;
|
|
37
|
+
getLoadedWidth: (marker: MarkerListItem) => string;
|
|
38
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import BaseFoundation from '../base/foundation';
|
|
2
|
+
export default class VideoProgressFoundation extends BaseFoundation {
|
|
3
|
+
constructor(adapter) {
|
|
4
|
+
var _this;
|
|
5
|
+
super(Object.assign({}, adapter));
|
|
6
|
+
_this = this;
|
|
7
|
+
this.handleDocumentMouseMove = e => {
|
|
8
|
+
const {
|
|
9
|
+
isDragging
|
|
10
|
+
} = this.getStates();
|
|
11
|
+
if (isDragging) {
|
|
12
|
+
this.handleMouseEvent(e, true);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
this.handleDocumentMouseUp = () => {
|
|
16
|
+
const {
|
|
17
|
+
isDragging
|
|
18
|
+
} = this.getStates();
|
|
19
|
+
if (isDragging) {
|
|
20
|
+
this._adapter.setIsDragging(false);
|
|
21
|
+
}
|
|
22
|
+
document.removeEventListener('mousemove', this.handleDocumentMouseMove);
|
|
23
|
+
document.removeEventListener('mouseup', this.handleDocumentMouseUp);
|
|
24
|
+
};
|
|
25
|
+
this.handleMouseDown = e => {
|
|
26
|
+
this._adapter.setIsDragging(true);
|
|
27
|
+
this.handleMouseEvent(e, true);
|
|
28
|
+
document.addEventListener('mousemove', this.handleDocumentMouseMove);
|
|
29
|
+
document.addEventListener('mouseup', this.handleDocumentMouseUp);
|
|
30
|
+
};
|
|
31
|
+
this.handleMouseUp = () => {
|
|
32
|
+
const {
|
|
33
|
+
isDragging
|
|
34
|
+
} = this.getStates();
|
|
35
|
+
if (isDragging) {
|
|
36
|
+
this._adapter.setIsDragging(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
this.handleMouseEvent = function (e) {
|
|
40
|
+
let shouldSetValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
41
|
+
const {
|
|
42
|
+
isDragging
|
|
43
|
+
} = _this.getStates();
|
|
44
|
+
const {
|
|
45
|
+
onChange,
|
|
46
|
+
max
|
|
47
|
+
} = _this.getProps();
|
|
48
|
+
const sliderRef = _this._adapter.getSliderRef();
|
|
49
|
+
if (!sliderRef) return;
|
|
50
|
+
const rect = sliderRef.getBoundingClientRect();
|
|
51
|
+
const offset = e.clientX - rect.left;
|
|
52
|
+
const total = rect.width;
|
|
53
|
+
const percentage = Math.min(Math.max(offset / total, 0), 1);
|
|
54
|
+
const value = percentage * max;
|
|
55
|
+
if (shouldSetValue && (isDragging || e.type === 'mousedown')) {
|
|
56
|
+
_this.setActiveIndex(value);
|
|
57
|
+
onChange(value);
|
|
58
|
+
}
|
|
59
|
+
_this._adapter.setMovingInfo({
|
|
60
|
+
progress: percentage,
|
|
61
|
+
offset: offset - rect.width / 2,
|
|
62
|
+
value
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
this.handleSliderMouseEnter = index => {
|
|
66
|
+
const {
|
|
67
|
+
value: currentValue
|
|
68
|
+
} = this.getProps();
|
|
69
|
+
const markersList = this._adapter.getMarkersList();
|
|
70
|
+
const currentSlider = markersList[index];
|
|
71
|
+
if (currentSlider.start < currentValue && currentSlider.end > currentValue) {
|
|
72
|
+
this._adapter.setIsHandleHovering(true);
|
|
73
|
+
} else {
|
|
74
|
+
this._adapter.setIsHandleHovering(false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
this.handleSliderMouseLeave = index => {
|
|
78
|
+
const {
|
|
79
|
+
value: currentValue
|
|
80
|
+
} = this.getProps();
|
|
81
|
+
const markersList = this._adapter.getMarkersList();
|
|
82
|
+
const currentSlider = markersList[index];
|
|
83
|
+
if (currentSlider.start < currentValue && currentSlider.end > currentValue) {
|
|
84
|
+
this._adapter.setIsHandleHovering(false);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
this.setActiveIndex = currentValue => {
|
|
88
|
+
const markersList = this._adapter.getMarkersList();
|
|
89
|
+
markersList.map((marker, index) => {
|
|
90
|
+
if (currentValue < marker.end && currentValue > marker.start) {
|
|
91
|
+
this._adapter.setIsHandleHovering(true);
|
|
92
|
+
this._adapter.setActiveIndex(index);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
this.getValueWidth = (marker, value) => {
|
|
97
|
+
const {
|
|
98
|
+
start,
|
|
99
|
+
end
|
|
100
|
+
} = marker;
|
|
101
|
+
if (value > end) {
|
|
102
|
+
return 'calc(100% - 2px)';
|
|
103
|
+
} else if (value < start) {
|
|
104
|
+
return '0%';
|
|
105
|
+
} else {
|
|
106
|
+
return `${(value - start) / (end - start) * 100}%`;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// Get the width of the video being played
|
|
110
|
+
this.getPlayedWidth = marker => {
|
|
111
|
+
const {
|
|
112
|
+
value: currentValue
|
|
113
|
+
} = this.getProps();
|
|
114
|
+
return this.getValueWidth(marker, currentValue);
|
|
115
|
+
};
|
|
116
|
+
this.getLoadedWidth = marker => {
|
|
117
|
+
const {
|
|
118
|
+
bufferedValue
|
|
119
|
+
} = this.getProps();
|
|
120
|
+
return this.getValueWidth(marker, bufferedValue);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Color
|
|
2
|
+
$color-videoPlayer_theme_dark-text: var(--semi-color-bg-0); // dark theme 字体色
|
|
3
|
+
$color-videoPlayer_theme_dark-bg: rgba(var(--semi-grey-8), 1); // dark theme 背景色
|
|
4
|
+
$color-videoPlayer_theme_light-text: rgba(var(--semi-grey-8), 1); // light theme 字体色
|
|
5
|
+
$color-videoPlayer_theme_light-bg: var(--semi-color-disabled-bg); // light theme 背景色
|
|
6
|
+
$color-videoPlayer_pause-bg: var(--semi-color-text-1); // 暂停按钮颜色
|
|
7
|
+
$color-videoPlayer_notification-bg: var(--semi-color-overlay-bg); // notification 背景色
|
|
8
|
+
$color-videoPlayer_notification-text: var(--semi-color-default); // notification 字体色
|
|
9
|
+
$color-videoPlayer_controls-bg: rgba(28, 31, 35, 0.8); // 控制栏背景色
|
|
10
|
+
$color-videoPlayer_controls_item-bg: var(--semi-color-overlay-bg); // 控制栏 item 背景色
|
|
11
|
+
$color-videoPlayer_controls-text: #fff; // 控制栏文字色
|
|
12
|
+
$color-videoPlayer_controls_item_popup-bg-default: var(--semi-color-overlay-bg); // 控制栏弹层背景色
|
|
13
|
+
$color-videoPlayer_controls_item_popup-bg-hover: rgba(67, 68, 74, 1); // 控制栏弹层背景色 - hover
|
|
14
|
+
$color-videoPlayer_controls_popup_item-text-default: rgba(#fff, 0.7); // 控制栏文字色
|
|
15
|
+
$color-videoPlayer_controls_popup_item-text-active: var(--semi-color-primary); // 控制栏弹层文字色 - 选中
|
|
16
|
+
$color-videoPlayer_progress_bar-bg-played: var(--semi-color-primary); // 进度条已播放部分背景色
|
|
17
|
+
$color-videoPlayer_progress_bar-bg-loaded: rgba(var(--semi-grey-3), 1); // 进度条已加载部分背景色
|
|
18
|
+
$color-videoPlayer_progress_bar-bg-unplayed: rgba(var(--semi-grey-5), 1); // 进度条未播放部分背景色
|
|
19
|
+
$color-videoPlayer_progress_bar_handle-bg: #fff; // handle 背景色
|
|
20
|
+
$color-videoPlayer_progress_bar_handle-border: var(--semi-color-primary); // handle 边框色
|
|
21
|
+
$color-videoPlayer_progress_bar_handle-shadow: var(--semi-color-shadow); // handle 阴影色
|
|
22
|
+
|
|
23
|
+
// Width/Height
|
|
24
|
+
$height-videoPlayer_progress_bar_hotSpot-default: 20px; // 进度条热区 default 高度
|
|
25
|
+
$height-videoPlayer_progress_bar-default: 4px; // 进度条 default 高度
|
|
26
|
+
$height-videoPlayer_progress_bar-hover: 10px; // 进度条 hover 高度
|
|
27
|
+
$height-videoPlayer_progress_bar_handle: 16px; // 进度条 handle 高度
|
|
28
|
+
$height-videoPlayer_controls_menu-default: 56px; // 控制栏 default 高度
|
|
29
|
+
$height-videoPlayer_controls_volume-default: 160px; // 控制栏音量 default 高度
|
|
30
|
+
$height-videoPlayer_controls_popup-default: 24px; // 控制栏弹层类元素 default 高度
|
|
31
|
+
$height-videoPlayer_controls_popup_item-default: 32px; // 控制栏弹层类元素 item default 高度
|
|
32
|
+
$width-videoPlayer_controls_volume-default: 40px; // 控制栏音量 default 宽度
|
|
33
|
+
$width-videoPlayer_controls_popup_item-default: 50px; // 控制栏弹层类元素 default 宽度
|
|
34
|
+
$width-videoPlayer_controls_popup-default: 48px; // 控制栏弹层 default 宽度
|
|
35
|
+
|
|
36
|
+
// Spacing
|
|
37
|
+
$spacing-videoPlayer-progress_bar_chapter-marginRight: 2px; // 进度条分节间距
|
|
38
|
+
$spacing-videoPlayer_notification-bottom: 22px; // notification 距离菜单栏距离
|
|
39
|
+
$spacing-videoPlayer_notification-left: 8px; // notification 距离菜单栏左侧距离
|
|
40
|
+
$spacing-videoPlayer_notification_text-paddingY: 8px; // notification 文字垂直方向 padding
|
|
41
|
+
$spacing-videoPlayer_notification_text-paddingX: 12px; // notification 文字水平方向 padding
|
|
42
|
+
$spacing-videoPlayer-controls-padding: $spacing-base-tight $spacing-base; // 控制栏 padding
|
|
43
|
+
$spacing-videoPlayer-controls_item-gap: $spacing-tight; // 控制栏 item 间距
|
|
44
|
+
$spacing-videoPlayer-controls_time-paddingX: 8px; // 控制栏时间 paddingX
|
|
45
|
+
$spacing-videoPlayer-controls_volume_title-paddingX: 10px; // 控制栏音量标题文字 paddingX
|
|
46
|
+
$spacing-videoPlayer-controls_volume_title-paddingY: 0px; // 控制栏音量标题文字 paddingX
|
|
47
|
+
$spacing-videoPlayer-controls_volume_popup-paddingY: 0px; // 控制栏音量弹层 paddingX
|
|
48
|
+
$spacing-videoPlayer-controls_volume_popup-paddingX: $spacing-extra-tight; // 控制栏音量弹层 paddingX
|
|
49
|
+
$spacing-videoPlayer-controls_popup-paddingX: $spacing-tight; // 控制栏弹层元素 paddingX
|
|
50
|
+
$spacing-videoPlayer-controls_popup-paddingY: 0px; // 控制栏弹层元素 paddingY
|
|
51
|
+
$spacing-videoPlayer-progress_bar_wrapper-marginX: $spacing-tight; // 进度条 wrapper marginX
|
|
52
|
+
$spacing-videoPlayer-progress_bar_wrapper-marginY: 0px; // 进度条 wrapper marginY
|
|
53
|
+
$spacing-videoPlayer-progress_bar_tooltip-top: 6px; // 进度条 Tooltip top 值
|
|
54
|
+
$spacing-videoPlayer_progress_bar_handle-top: 15px; // 进度条 handle top 值
|
|
55
|
+
$spacing-videoPlayer_error_svg-marginBottom: 12px; // error svg marginBottom
|
|
56
|
+
|
|
57
|
+
// Radius
|
|
58
|
+
$radius-videoPlayer_notification: 3px; // notification 圆角
|
|
59
|
+
$radius-videoPlayer_progress_bar_handle: 50%; // 进度条 handle 圆角
|
|
60
|
+
$radius-videoPlayer_progress_bar: 999px; // 进度条圆角
|
|
61
|
+
$radius-videoPlayer_controls_item: 3px; // 控制栏 item 圆角
|
|
62
|
+
$radius-videoPlayer_controls_popup: 4px; // 控制栏弹层圆角
|
|
63
|
+
|
|
64
|
+
// Font
|
|
65
|
+
$font-videoPlayer_notification-fontSize: $font-size-regular; // notification 字体大小
|
|
66
|
+
$font-videoPlayer_controls_item-fontSize: $font-size-small; // 控制栏 item 字体大小
|
|
67
|
+
$font-videoPlayer_controls_time_text-fontSize: $font-size-regular; // 控制栏时间字体大小
|
|
68
|
+
$font-videoPlayer_error-fontSize: $font-size-regular; // error 字体大小
|
|
69
|
+
$font-videoPlayer_notification-lineHeight: 20px; // notification 行高
|
|
70
|
+
$font-videoPlayer_controls_popup_item-lineHeight: 16px; // 控制栏 item 行高
|
|
71
|
+
$font-videoPlayer_controls_popup_item-fontWeight: 600; // 控制栏 item 字体粗细
|
|
72
|
+
$font-videoPlayer_error-fontWeight: 600; // error 字体粗细
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|