tencent.jquery.pix.component 1.0.53

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.
@@ -0,0 +1,355 @@
1
+ import { $, windowEnv } from "../config";
2
+ import VideoHTML from "./videohtml";
3
+ import './videocss.scss'
4
+
5
+ /**
6
+ * 在页面上放置一个视频播放器组件(腾讯视频)
7
+ * @constructor
8
+ * @param {object} options 选项
9
+ * @param {string} options.container 容器元素的jquery选择器
10
+ * @param {string} options.vid 腾讯视频id
11
+ * @param {string} [options.previewUrl] 视频预览图
12
+ * @param {0|1} [options.videoType=1] 视频类型选项,0: 标清,1: 高清
13
+ * @param {number} [options.autoHideControlsDelayMs=5000] 自动隐藏控制条延迟时间,单位ms
14
+ * @param {boolean} [options.showProgressBar=true] 是否显示进度条
15
+ */
16
+ export function VideoPlayer(options) {
17
+ this.options = options
18
+ this.init()
19
+ }
20
+
21
+ VideoPlayer.prototype.init = async function() {
22
+ if (windowEnv === 'h5') {
23
+ this.durationFactor = 1; // 浏览器,单位s
24
+ } else {
25
+ this.durationFactor = 1000; // PixUI,单位ms
26
+ }
27
+
28
+ const $container = $(this.options.container);
29
+ this.$container = $container;
30
+ $container.html(VideoHTML);
31
+
32
+ if (this.options.previewUrl) {
33
+ $container.find('.myplayer-video-preview').css('background-image', `url(${this.options.previewUrl})`);
34
+ }
35
+
36
+ // 缺省选项
37
+ this.options = {
38
+ videoType: 1,
39
+ autoHideControlsDelayMs: 5000,
40
+ showProgressBar: true,
41
+ ...this.options
42
+ }
43
+
44
+ const videoURL = await getVideoURL(this.options.vid, this.options.videoType);
45
+ $container.find('.myplayer-video-mask').before(`<video src="${videoURL}"></video>`);
46
+
47
+ this.state = {
48
+ playing: false,
49
+ controlsVisible: false,
50
+ updatingProgress: false,
51
+ controlsDelayStart: 0
52
+ }
53
+
54
+ // 根据showProgressBar选项控制进度条显示
55
+ if (!this.options.showProgressBar) {
56
+ $container.find('.myplayer-progress').hide();
57
+ }
58
+
59
+ this.bindEvent();
60
+ }
61
+
62
+ VideoPlayer.prototype.bindEvent = function() {
63
+ const container = this.$container;
64
+ container.off()
65
+ .on('click', '.myplayer-video-cover', () => {
66
+ container.find('.myplayer-video-cover,.myplayer-video-preview').remove();
67
+ this.updateTotalTime();
68
+ this.play();
69
+ })
70
+ .on('click', '.myplayer-video-mask', () => {
71
+ this.maskClicked();
72
+ })
73
+ .on('click', '.myplayer-btn-play', () => {
74
+ this.play();
75
+ })
76
+ .on('click', '.myplayer-btn-pause', () => {
77
+ this.pause();
78
+ })
79
+ .on('click', '.myplayer-btn-full-screen', () => {
80
+ this.toggleFullScreen();
81
+ })
82
+ .on('click', '.myplayer-btn-exit-full-screen', () => {
83
+ this.toggleFullScreen();
84
+ })
85
+ .on('canplay', 'video', () => {
86
+ console.log('canplay');
87
+ this.updateTotalTime();
88
+ });
89
+
90
+ // 只有在显示进度条时才绑定进度条相关事件
91
+ if (this.options.showProgressBar) {
92
+ container
93
+ .on('dragstart', '.myplayer-progress', (e) => {
94
+ if (e.originalEvent) {
95
+ this.progressDragStart(e.originalEvent);
96
+ } else {
97
+ this.progressDragStart(e);
98
+ }
99
+ })
100
+ .on('drag', '.myplayer-progress', (e) => {
101
+ if (e.originalEvent) {
102
+ this.progressDrag(e.originalEvent);
103
+ } else {
104
+ this.progressDrag(e);
105
+ }
106
+ })
107
+ .on('dragend', '.myplayer-progress', (e) => {
108
+ if (e.originalEvent) {
109
+ this.progressDragEnd(e.originalEvent);
110
+ } else {
111
+ this.progressDragEnd(e);
112
+ }
113
+ })
114
+ .on('click', '.myplayer-progress', (e) => {
115
+ if (e.originalEvent) {
116
+ this.progressDragEnd(e.originalEvent);
117
+ } else {
118
+ this.progressDragEnd(e);
119
+ }
120
+ });
121
+ }
122
+ }
123
+
124
+ VideoPlayer.prototype.play = function() {
125
+ const video = this.$container.find('video')[0];
126
+ video.play();
127
+ this.state.playing = true;
128
+ this.$container.find('.myplayer-btn-play').hide();
129
+ this.$container.find('.myplayer-btn-big-play').addClass('myplayer-transparent');
130
+ this.$container.find('.myplayer-btn-pause').show();
131
+ this.$container.find('.myplayer-progress').css('transform', 'none');
132
+ this.startUpdatingProgress();
133
+ this.hideControlsWithDelay();
134
+ }
135
+
136
+ VideoPlayer.prototype.pause = function() {
137
+ const video = this.$container.find('video')[0];
138
+ video.pause();
139
+ this.state.playing = false;
140
+ this.$container.find('.myplayer-btn-play').show();
141
+ this.$container.find('.myplayer-btn-big-play').removeClass('myplayer-transparent');
142
+ this.$container.find('.myplayer-btn-pause').hide();
143
+ this.$container.find('.myplayer-progress').css('transform', 'scaleY(2)');
144
+ this.state.controlsDelayStart = Date.now();
145
+ }
146
+
147
+ VideoPlayer.prototype.maskClicked = function() {
148
+ if (this.state.controlsVisible === true) {
149
+ if (this.state.playing === true) {
150
+ this.pause();
151
+ } else {
152
+ this.play();
153
+ }
154
+ } else {
155
+ this.showControls();
156
+ }
157
+ }
158
+
159
+ VideoPlayer.prototype.showControls = function() {
160
+ const container = this.$container;
161
+ container.find('.myplayer-video-mask').removeClass('myplayer-transparent');
162
+ container.find('.myplayer-top').removeClass('myplayer-transparent');
163
+ container.find('.myplayer-bottom').removeClass('myplayer-transparent');
164
+ this.state.controlsVisible = true;
165
+ this.state.updatingProgress = true;
166
+ this.updateTotalTime();
167
+ this.startUpdatingProgress();
168
+
169
+ this.hideControlsWithDelay();
170
+ }
171
+
172
+ VideoPlayer.prototype.hideControls = function() {
173
+ const container = this.$container;
174
+ container.find('.myplayer-video-mask').addClass('myplayer-transparent');
175
+ container.find('.myplayer-top').addClass('myplayer-transparent');
176
+ container.find('.myplayer-bottom').addClass('myplayer-transparent');
177
+ this.state.controlsVisible = false;
178
+ this.state.updatingProgress = false;
179
+ }
180
+
181
+ VideoPlayer.prototype.hideControlsWithDelay = function() {
182
+ this.state.controlsDelayStart = Date.now();
183
+ setTimeout(() => {
184
+ const nowTime = Date.now();
185
+ console.debug("nowTime - this.state.controlsDelayStart", nowTime - this.state.controlsDelayStart);
186
+
187
+ // 有时正常时间差也会小于设定的时间,所以判断时减去100ms
188
+ if (this.state.playing === true && nowTime - this.state.controlsDelayStart >= this.options.autoHideControlsDelayMs - 100) {
189
+ this.hideControls();
190
+ }
191
+ }, this.options.autoHideControlsDelayMs);
192
+ }
193
+
194
+ VideoPlayer.prototype.startUpdatingProgress = function() {
195
+ this.updateProgress();
196
+
197
+ if (this.state.updatingProgress) {
198
+ window.requestAnimationFrame(this.startUpdatingProgress.bind(this));
199
+ }
200
+ }
201
+
202
+ VideoPlayer.prototype.updateProgress = function() {
203
+ const video = this.$container.find('video')[0];
204
+ const currentTime = video.currentTime;
205
+ const duration = video.duration;
206
+ const progress = currentTime / duration;
207
+
208
+ const parentWidth = this.$container.find('.myplayer-progress').width();
209
+ this.$container.find('.myplayer-subprogress').width(`${progress * parentWidth}px`);
210
+
211
+ this.$container.find('.myplayer-playtime').html(formatTime(currentTime / this.durationFactor));
212
+ }
213
+
214
+ VideoPlayer.prototype.updateTotalTime = function() {
215
+ const video = this.$container.find('video')[0];
216
+ const duration = video.duration;
217
+
218
+ this.$container.find('.myplayer-totaltime').html(formatTime(duration / this.durationFactor));
219
+ }
220
+
221
+ VideoPlayer.prototype.toggleFullScreen = function() {
222
+ const $container = this.$container;
223
+ const fullScreenBtn = $container.find('.myplayer-btn-full-screen');
224
+ const exitFullScreenBtn = $container.find('.myplayer-btn-exit-full-screen');
225
+ const bottom = $container.find('.myplayer-bottom');
226
+ const innerContainer = $container.find('.myplayer-container');
227
+
228
+ if (innerContainer.hasClass('myplayer-full-screen')) {
229
+ innerContainer.removeClass('myplayer-full-screen');
230
+ bottom.removeClass('myplayer-full-screen');
231
+ fullScreenBtn.show();
232
+ exitFullScreenBtn.hide();
233
+ } else {
234
+ innerContainer.addClass('myplayer-full-screen');
235
+ bottom.addClass('myplayer-full-screen');
236
+ fullScreenBtn.hide();
237
+ exitFullScreenBtn.show();
238
+ }
239
+
240
+ this.hideControlsWithDelay();
241
+ }
242
+
243
+ /**
244
+ * 进度条拖动开始
245
+ * @param {MouseEvent} e
246
+ */
247
+ VideoPlayer.prototype.progressDragStart = function(e) {
248
+ if (!this.options.showProgressBar) {
249
+ return;
250
+ }
251
+
252
+ this.state.updatingProgress = false;
253
+ if (this.state.playing === true) {
254
+ this.pause();
255
+ }
256
+ }
257
+
258
+ /**
259
+ * 进度条拖动
260
+ * @param {MouseEvent} e
261
+ */
262
+ VideoPlayer.prototype.progressDrag = function(e) {
263
+ if (!this.options.showProgressBar) {
264
+ return;
265
+ }
266
+
267
+ const $container = this.$container;
268
+ const progress = $container.find('.myplayer-progress');
269
+ const subprogress = $container.find('.myplayer-subprogress');
270
+ const video = $container.find('video')[0];
271
+
272
+ const parentWidth = progress.width();
273
+ const curOffset = Math.max(Math.min(e.offsetX, parentWidth), 0);
274
+ subprogress.width(`${curOffset}px`);
275
+ const adjustedPlayTime = curOffset / parentWidth * video.duration;
276
+ $container.find('.myplayer-playtime').html(formatTime(adjustedPlayTime / this.durationFactor));
277
+ }
278
+
279
+ /**
280
+ * 进度条拖动结束
281
+ * @param {MouseEvent} e
282
+ */
283
+ VideoPlayer.prototype.progressDragEnd = function(e) {
284
+ if (!this.options.showProgressBar) {
285
+ return;
286
+ }
287
+
288
+ const $container = this.$container;
289
+ const video = $container.find('video')[0];
290
+ const progress = $container.find('.myplayer-progress');
291
+ const parentWidth = progress.width();
292
+ const curOffset = Math.max(Math.min(e.offsetX, parentWidth), 0);
293
+
294
+ // currentTime set 单位s, get 单位ms
295
+ video.currentTime = curOffset / progress.width() * video.duration / this.durationFactor;
296
+
297
+ this.play();
298
+
299
+ this.state.updatingProgress = true;
300
+ this.startUpdatingProgress();
301
+ }
302
+
303
+ /**
304
+ * 获取视频真实地址
305
+ * @param {string} vid 腾讯视频vid
306
+ * @param {0|1} [type] 类型,0: 标清,1: 高清
307
+ * @returns {Promise<string>} 真实视频地址
308
+ */
309
+ const getVideoURL = async function(vid, type = 1) {
310
+ const sApiUrl = `https://vv.video.qq.com/getinfo?vid=${vid}&platform=101001&charge=0&otype=json&t=${Date.now()}`;
311
+
312
+ try {
313
+ console.log('processing vid through sApiUrl: ', sApiUrl);
314
+ const res = await fetchData(sApiUrl);
315
+ console.log('222 processing vid through sApiUrl: ', sApiUrl);
316
+ const processedStr = res.replace('QZOutputJson=', '');
317
+ const jsonStr = processedStr.substring(0, processedStr.length - 1);
318
+ const resObj = JSON.parse(jsonStr);
319
+ const sRealUrl = `${resObj.vl.vi[0].ul.ui[type].url}${resObj.vl.vi[0].fn}?vkey=${resObj.vl.vi[0].fvkey}`;
320
+ console.log('腾讯视频真实地址: ', sRealUrl);
321
+ return sRealUrl;
322
+ } catch (err) {
323
+ console.error(err)
324
+ return '';
325
+ }
326
+ }
327
+
328
+ /**
329
+ * 异步get请求
330
+ * @param {string} url
331
+ * @returns {Promise<string>}
332
+ */
333
+ const fetchData = function(url) {
334
+ const xhr = new XMLHttpRequest();
335
+ xhr.open('GET', url);
336
+ xhr.send();
337
+
338
+ return new Promise((resolve, reject) => {
339
+ xhr.onreadystatechange = function () {
340
+ if (xhr.readyState === 4) {
341
+ if (xhr.status === 200) {
342
+ resolve(xhr.responseText); // 请求成功,返回响应内容
343
+ } else {
344
+ reject(new Error('请求失败')); // 请求失败,返回错误对象
345
+ }
346
+ }
347
+ };
348
+ });
349
+ }
350
+
351
+ const formatTime = function(secs) {
352
+ const minutes = Math.floor(secs / 60);
353
+ const seconds = Math.floor(secs % 60);
354
+ return `${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
355
+ }