videojs-mobile-ui 1.2.0-alpha.0 → 1.2.1

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.
Files changed (53) hide show
  1. package/README.md +104 -53
  2. package/dist/lang/de.js +1 -3
  3. package/dist/lang/en.js +1 -3
  4. package/dist/lang/it.js +1 -3
  5. package/dist/videojs-mobile-ui.cjs.js +195 -43
  6. package/dist/videojs-mobile-ui.css +104 -2
  7. package/dist/videojs-mobile-ui.es.js +195 -43
  8. package/dist/videojs-mobile-ui.js +204 -53
  9. package/dist/videojs-mobile-ui.min.js +2 -2
  10. package/docs/api/TouchOverlay.html +964 -0
  11. package/docs/api/fonts/OpenSans-Bold-webfont.eot +0 -0
  12. package/docs/api/fonts/OpenSans-Bold-webfont.svg +1830 -0
  13. package/docs/api/fonts/OpenSans-Bold-webfont.woff +0 -0
  14. package/docs/api/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  15. package/docs/api/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  16. package/docs/api/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  17. package/docs/api/fonts/OpenSans-Italic-webfont.eot +0 -0
  18. package/docs/api/fonts/OpenSans-Italic-webfont.svg +1830 -0
  19. package/docs/api/fonts/OpenSans-Italic-webfont.woff +0 -0
  20. package/docs/api/fonts/OpenSans-Light-webfont.eot +0 -0
  21. package/docs/api/fonts/OpenSans-Light-webfont.svg +1831 -0
  22. package/docs/api/fonts/OpenSans-Light-webfont.woff +0 -0
  23. package/docs/api/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  24. package/docs/api/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  25. package/docs/api/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  26. package/docs/api/fonts/OpenSans-Regular-webfont.eot +0 -0
  27. package/docs/api/fonts/OpenSans-Regular-webfont.svg +1831 -0
  28. package/docs/api/fonts/OpenSans-Regular-webfont.woff +0 -0
  29. package/docs/api/global.html +957 -0
  30. package/docs/api/index.html +159 -0
  31. package/docs/api/plugin.js.html +221 -0
  32. package/docs/api/scripts/linenumber.js +25 -0
  33. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  34. package/docs/api/scripts/prettify/lang-css.js +2 -0
  35. package/docs/api/scripts/prettify/prettify.js +28 -0
  36. package/docs/api/styles/jsdoc-default.css +358 -0
  37. package/docs/api/styles/prettify-jsdoc.css +111 -0
  38. package/docs/api/styles/prettify-tomorrow.css +132 -0
  39. package/docs/api/swipeFullscreen.js.html +173 -0
  40. package/docs/api/touchOverlay.js.html +211 -0
  41. package/index.html +238 -170
  42. package/package.json +23 -13
  43. package/scripts/lang.js +24 -0
  44. package/scripts/netlify.js +16 -0
  45. package/scripts/postcss.config.js +29 -5
  46. package/scripts/readme-options.js +370 -0
  47. package/scripts/rollup.config.js +0 -8
  48. package/src/plugin.css +6 -0
  49. package/src/plugin.js +65 -39
  50. package/src/swipeFullscreen.js +122 -0
  51. package/src/touchOverlay.js +7 -3
  52. package/test/plugin.test.js +125 -18
  53. package/test/swipeFullscreen.test.js +365 -0
package/README.md CHANGED
@@ -1,51 +1,54 @@
1
1
  # videojs-mobile-ui
2
2
 
3
- Mobile UI for Video.js.
3
+ A more native mobile user experience for Video.js.
4
4
 
5
- Touch controls:
5
+ **videojs-mobile-ui** augments the standard Video.js experience into a touch-optimized, mobile-first interface. It adds the intuitive gestures and smart behaviors users expect from top-tier video apps to your browser-based player.
6
6
 
7
- - Double-tap the left side of the player to rewind ten seconds
8
- - Double-tap the right side of the player to fast-forward ten seconds
9
- - Single-tap the screen to show a play/pause toggle
7
+ ## Key Features
10
8
 
11
- Fullscreen control:
9
+ ### 👆 Touch Controls
12
10
 
13
- - Rotate to landscape to enter Fullscreen
14
- - Lock to fullscreen on rotate
15
- - Always lock to landscape when entering fullscreen (works even when device rotation is disabled/non-functional)
11
+ Double-tap to Seek: Just like popular video apps, users can double-tap the left or right side of the video to rewind or fast-forward.
16
12
 
17
- ## Table of Contents
13
+ Large play/pause overlay: A large, screen-wide touch zone allows for easy Play/Pause toggling without hunting for tiny buttons.
18
14
 
19
- <!-- START doctoc generated TOC please keep comment here to allow auto update -->
20
- <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
21
- ## Installation
15
+ ### 📱 Fullscreen Orientation:
22
16
 
23
- - [Installation](#installation)
24
- - [Plugin Options](#plugin-options)
25
- - [Default options](#default-options)
26
- - [Options](#options)
27
- - [Usage](#usage)
28
- - [`<script>` Tag](#script-tag)
29
- - [Browserify/CommonJS](#browserifycommonjs)
30
- - [RequireJS/AMD](#requirejsamd)
31
- - [License](#license)
32
-
33
- <!-- END doctoc generated TOC please keep comment here to allow auto update -->
34
- ## Installation
17
+ Rotate to Watch: Automatically enter fullscreen when the user rotates their phone to landscape.
35
18
 
36
- ```sh
37
- npm install video.js
38
- npm install videojs-mobile-ui
39
- ```
19
+ Orientation Lock: Keeps the video fullscreen and locked to landscape mode even if the user tilts their phone back slightly, preventing accidental exits (works on supported devices).
40
20
 
41
- Version 1.x requires video.js 8.x as a peer dependency. Lowever video.js versions are not supported. 0.7.0 supports video.js 7.x. To install the latest version that works with Video.js 7, use the `latest7` tag:
21
+ ### 🚀 Swipe Gestures (Optional)
42
22
 
43
- ```sh
44
- npm install videojs-mobile-ui@latest7
45
- ```
23
+ Enable modern swipe gestures to control the viewing mode.
24
+
25
+ Swipe Up to enter fullscreen with a smooth zoom-in effect.
26
+
27
+ Swipe Down to exit fullscreen naturally.
28
+
29
+ ### 🎨 Configuration Options
30
+
31
+ Visual indicators show exactly how many seconds are being skipped during a seek.
32
+
33
+ Adjust seek times and tap sensitivity to match your specific content needs.
34
+
35
+ ## Compatibility Notes
36
+
37
+ - iOS Safari does not support orientation lock. The fullscreen video on iOS is native and not influenced by this plugin.
38
+ - Android Firefox has native rotate and lock behaviour when an element containing a video is made fullscreen, which will override this plugin.
46
39
 
47
40
  ## Plugin Options
48
41
 
42
+ Newer functionality is opt-in, to not force new features on existing players. Things you might want to add:
43
+
44
+ - `fullscreen.swipeToFullscreen`, to enter fullscreen by swiping up on the video.
45
+ - `fullscreen.swipeFromFullscreen`, to exit fullscreen by swiping down on the video (except iPhone).
46
+ - `touchControls.disableOnEnd`, to disable the touch controls at the end of the video. Useful if you have any sort of endcard displayed at the end of the video that might otherwise conflict.
47
+
48
+ The [demo] page lets you try out the configuration options.
49
+
50
+ ![QR code link to demo page][demo-qr]
51
+
49
52
  ### Default options
50
53
 
51
54
  ```js
@@ -55,30 +58,70 @@ npm install videojs-mobile-ui@latest7
55
58
  exitOnRotate: true,
56
59
  lockOnRotate: true,
57
60
  lockToLandscapeOnEnter: false,
61
+ swipeToFullscreen: false,
62
+ swipeFromFullscreen: false,
58
63
  disabled: false
59
64
  },
60
65
  touchControls: {
61
66
  seekSeconds: 10,
62
67
  tapTimeout: 300,
63
68
  disableOnEnd: false,
64
- disabled: false,
69
+ disabled: false
65
70
  }
66
71
  };
67
72
  ```
68
73
 
69
74
  ### Options
70
75
 
71
- - *fullscreen.enterOnRotate* `boolean` Whether to go fullscreen when rotating to landscape
72
- - *fullscreen.exitOnRotate* `boolean` Whether to leave fullscreen when rotating to portrait (if not locked)
73
- - *fullscreen.lockOnRotate* `boolean` Whether to lock to fullscreen when rotating to landscape
74
- - *fullscreen.lockToLandscapeOnEnter* `boolean` Whether to lock to landscape when entering fullscreen (works even when device rotation is disabled/non-functional)
75
- - *fullscreen.disabled* `boolean` If true no fullscreen handling except the *deprecated* iOS fullwindow hack
76
- - *touchControls.seekSeconds* `int` Seconds to seek when double-tapping
77
- - *touchControls.tapTimeout* `int` Milliseconds to consider a double-tap
78
- - *touchControls.disableOnEnd* `boolean` Whether to disable touch controls when the video has ended, e.g. if an endscreen is used. Automatically disables if the endscreen plugin is present when this plugin initialises
79
- - *touchControls.disabled* `boolean` If true no touch controls are added.
76
+ - **`fullscreen`** {Object}
77
+ Options for fullscreen behaviours.
78
+ - **`fullscreen.enterOnRotate`** {boolean}
79
+ If the device is rotated, enter fullscreen.
80
+ Default `true`.
81
+ - **`fullscreen.exitOnRotate`** {boolean}
82
+ If the device is rotated, exit fullscreen, unless `lockOnRotate` is used.
83
+ Default `true`.
84
+ - **`fullscreen.lockOnRotate`** {boolean}
85
+ When going fullscreen in response to rotation (`enterOnRotate`), also lock the orientation (not supported by iOS).
86
+ Default `true`.
87
+ - **`fullscreen.lockToLandscapeOnEnter`** {boolean}
88
+ When fullscreen is entered by any means, lock the orientation (not supported by iOS).
89
+ Default `false`.
90
+ - **`fullscreen.swipeToFullscreen`** {boolean}
91
+ Swipe up to enter fullscreen.
92
+ Default `false`.
93
+ - **`fullscreen.swipeFromFullscreen`** {boolean}
94
+ Swipe down to exit fullscreen.
95
+ Won't do anything on iOS native fullscreen, which has its own swipe down exit gesture.
96
+ Default `false`.
97
+ - **`fullscreen.disabled`** {boolean}
98
+ All fullscreen functionality provided by this plugin disabled.
99
+ Default `false`.
100
+ - **`touchControls`** {Object}
101
+ Options for tap overlay.
102
+ - **`touchControls.seekSeconds`** {number}
103
+ Increment to seek in seconds.
104
+ Default `10`.
105
+ - **`touchControls.tapTimeout`** {number}
106
+ Timeout to consider multiple taps as double rather than two single.
107
+ Default `300`.
108
+ - **`touchControls.disableOnEnd`** {boolean}
109
+ Disable the touch overlay when the video ends.
110
+ Useful if an end screen overlay is used to avoid conflict.
111
+ Default `false`.
112
+ - **`touchControls.disabled`** {boolean}
113
+ All tap overlay functionality provided by this plugin disabled.
114
+ Default `false`.
80
115
 
81
- ## Usage
116
+ ## Installation
117
+
118
+ Version 1.x requires video.js 8.x as a peer dependency. Lower video.js versions are not supported.
119
+
120
+ The last version to support video.js 7.x was 0.7.0. To install the latest version that works with Video.js 7, use the `latest7` tag:
121
+
122
+ ```sh
123
+ npm install videojs-mobile-ui@latest7
124
+ ```
82
125
 
83
126
  To include videojs-mobile-ui on your website or web application, use any of the following methods.
84
127
 
@@ -91,16 +134,23 @@ This is the simplest case. Get the script in whatever way you prefer and include
91
134
  <script src="//path/to/video.min.js"></script>
92
135
  <script src="//path/to/videojs-mobile-ui.min.js"></script>
93
136
  <script>
94
- var player = videojs('my-video');
95
-
96
- player.mobileUi();
137
+ const player = videojs('my-video');
138
+ const pluginOptions = {
139
+ {
140
+ fullscreen: {
141
+ swipeToFullscreen: true
142
+ }
143
+ }
144
+ };
145
+
146
+ player.mobileUi(pluginOptions);
97
147
  </script>
98
148
  ```
99
149
 
100
150
  The release versions will be available on jdselivr, unpkg etc.
101
151
 
102
- * https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.min.js
103
- * https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.css
152
+ - https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.min.js
153
+ - https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.css
104
154
 
105
155
  ### Browserify/CommonJS
106
156
 
@@ -119,7 +169,7 @@ var player = videojs('my-video');
119
169
  player.mobileUi();
120
170
  ```
121
171
 
122
- Also include the CSS.
172
+ Also include the CSS!
123
173
 
124
174
  ### RequireJS/AMD
125
175
 
@@ -133,7 +183,7 @@ require(['video.js', 'videojs-mobile-ui'], function(videojs) {
133
183
  });
134
184
  ```
135
185
 
136
- Also include the CSS.
186
+ Also include the CSS!
137
187
 
138
188
  ### Import
139
189
 
@@ -149,5 +199,6 @@ import 'videojs-mobile-ui';
149
199
 
150
200
  MIT. Copyright (c) mister-ben &lt;git@misterben.me&gt;
151
201
 
152
-
153
- [videojs]: http://videojs.com/
202
+ [videojs]: http://videojs.org/
203
+ [demo]: https://videojs-mobile-ui.netlify.app
204
+ [demo-qr]: /demo-qr.svg
package/dist/lang/de.js CHANGED
@@ -1,3 +1 @@
1
- videojs.addLanguage('de', {
2
- "seconds": "Sekunden"
3
- });
1
+ videojs.addLanguage('de', {"seconds":"Sekunden"});
package/dist/lang/en.js CHANGED
@@ -1,3 +1 @@
1
- videojs.addLanguage('en', {
2
- "seconds": "seconds"
3
- });
1
+ videojs.addLanguage('en', {"seconds":"seconds"});
package/dist/lang/it.js CHANGED
@@ -1,3 +1 @@
1
- videojs.addLanguage('it', {
2
- "seconds": "secondi"
3
- });
1
+ videojs.addLanguage('it', {"seconds":"secondi"});
@@ -1,20 +1,23 @@
1
- /*! @name videojs-mobile-ui @version 1.2.0-alpha.0 @license MIT */
1
+ /*! @name videojs-mobile-ui @version 1.2.1 @license MIT */
2
2
  'use strict';
3
3
 
4
4
  var videojs = require('video.js');
5
- var window = require('@ungap/global-this');
5
+ var window = require('global/window');
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
9
9
  var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
10
10
  var window__default = /*#__PURE__*/_interopDefaultLegacy(window);
11
11
 
12
- var version = "1.2.0-alpha.0";
12
+ var version = "1.2.1";
13
13
 
14
14
  /**
15
15
  * @file touchOverlay.js
16
16
  * Touch UI component
17
17
  */
18
+
19
+ /** @import Player from 'video.js/dist/types/player' */
20
+
18
21
  const Component = videojs__default["default"].getComponent('Component');
19
22
  const dom = videojs__default["default"].dom || videojs__default["default"];
20
23
 
@@ -30,7 +33,7 @@ class TouchOverlay extends Component {
30
33
  * @param {Player} player
31
34
  * The `Player` that this class should be attached to.
32
35
  *
33
- * @param {Object} [options]
36
+ * @param {options} [options]
34
37
  * The key/value store of player options.
35
38
  */
36
39
  constructor(player, options) {
@@ -119,7 +122,9 @@ class TouchOverlay extends Component {
119
122
  if (event.target !== this.el_) {
120
123
  return;
121
124
  }
122
- event.preventDefault();
125
+ if (event.cancelable) {
126
+ event.preventDefault();
127
+ }
123
128
  this.taps += 1;
124
129
  if (this.taps === 1) {
125
130
  this.removeClass('skip');
@@ -145,13 +150,184 @@ class TouchOverlay extends Component {
145
150
  }
146
151
  Component.registerComponent('TouchOverlay', TouchOverlay);
147
152
 
148
- // Default options for the plugin.
153
+ /** @import Player from 'video.js/dist/types/player' */
154
+ /** @import Plugin from 'video.js/dist/types/plugin' */
155
+ /** @import {MobileUiOptions} from './plugin' */
156
+
157
+ /**
158
+ * Sets up swiping to enter and exit fullscreen.
159
+ *
160
+ * @this {Plugin}
161
+ * @param {Player} player
162
+ * The player to initialise on.
163
+ * @param {MobileUiOptions} pluginOptions
164
+ * The options used by the mobile ui plugin.
165
+ */
166
+ const initSwipe = (player, pluginOptions) => {
167
+ const {
168
+ swipeToFullscreen,
169
+ swipeFromFullscreen
170
+ } = pluginOptions.fullscreen;
171
+ if (swipeToFullscreen) {
172
+ player.addClass('using-fs-swipe-up');
173
+ }
174
+ if (swipeFromFullscreen) {
175
+ player.addClass('using-fs-swipe-down');
176
+ }
177
+ let touchStartY = 0;
178
+ let couldBeSwiping = false;
179
+ const swipeThreshold = 30;
180
+
181
+ /**
182
+ * Monitor the possible start of a swipe
183
+ *
184
+ * @param {TouchEvent} e Triggering touch event
185
+ */
186
+ const onStart = e => {
187
+ const isFullscreen = player.isFullscreen();
188
+ if (!isFullscreen && !swipeToFullscreen || isFullscreen && !swipeFromFullscreen) {
189
+ couldBeSwiping = false;
190
+ return;
191
+ }
192
+ touchStartY = e.changedTouches[0].clientY;
193
+ couldBeSwiping = true;
194
+ player.tech_.el().style.transition = '';
195
+ };
196
+
197
+ /**
198
+ * Monitor the movement of a swipe
199
+ *
200
+ * @param {TouchEvent} e Triggering touch event
201
+ */
202
+ const onMove = e => {
203
+ if (!couldBeSwiping) {
204
+ return;
205
+ }
206
+ const currentY = e.touches[0].clientY;
207
+ const deltaY = touchStartY - currentY;
208
+ const isFullscreen = player.isFullscreen();
209
+ let scale = 1;
210
+ if (!isFullscreen && deltaY > 0) {
211
+ // Swiping up to enter fullscreen: Zoom in (Max 1.1)
212
+ scale = 1 + Math.min(0.1, deltaY / 500);
213
+ player.tech_.el().style.transform = `scale(${scale})`;
214
+ } else if (isFullscreen && deltaY < 0) {
215
+ // Swiping down to exit fullscreen: Zoom out (Min 0.9)
216
+ scale = 1 - Math.min(0.1, Math.abs(deltaY) / 500);
217
+ player.tech_.el().style.transform = `scale(${scale})`;
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Monitor the touch end to determine a valid swipe
223
+ *
224
+ * @param {TouchEvent} e Triggering touch event
225
+ */
226
+ const onEnd = e => {
227
+ if (!couldBeSwiping) {
228
+ return;
229
+ }
230
+ couldBeSwiping = false;
231
+ player.tech_.el().style.transition = 'transform 0.3s ease-out';
232
+ player.tech_.el().style.transform = 'scale(1)';
233
+ if (e.type === 'touchcancel') {
234
+ return;
235
+ }
236
+ const touchEndY = e.changedTouches[0].clientY;
237
+ const deltaY = touchStartY - touchEndY;
238
+ if (deltaY > swipeThreshold && !player.isFullscreen()) {
239
+ player.requestFullscreen().catch(err => {
240
+ player.log.warn('Browser refused fullscreen', err);
241
+ });
242
+ } else if (deltaY < -swipeThreshold && player.isFullscreen()) {
243
+ player.exitFullscreen();
244
+ }
245
+ };
246
+ player.el().addEventListener('touchstart', onStart, {
247
+ passive: true
248
+ });
249
+ player.el().addEventListener('touchmove', onMove, {
250
+ passive: true
251
+ });
252
+ player.el().addEventListener('touchend', onEnd, {
253
+ passive: true
254
+ });
255
+ player.el().addEventListener('touchcancel', onEnd, {
256
+ passive: true
257
+ });
258
+ player.on('dispose', () => {
259
+ player.el().removeEventListener('touchstart', onStart, {
260
+ passive: true
261
+ });
262
+ player.el().removeEventListener('touchmove', onMove, {
263
+ passive: true
264
+ });
265
+ player.el().removeEventListener('touchend', onEnd, {
266
+ passive: true
267
+ });
268
+ player.el().removeEventListener('touchcancel', onEnd, {
269
+ passive: true
270
+ });
271
+ player.tech_.el().style.transform = '';
272
+ player.tech_.el().style.transition = '';
273
+ });
274
+ };
275
+
276
+ /**
277
+ * @typedef {Object} MobileUiOptions
278
+ * @property {Object} [fullscreen]
279
+ * Options for fullscreen behaviours.
280
+ * @property {boolean} [fullscreen.enterOnRotate]
281
+ * If the device is rotated, enter fullscreen.
282
+ * Default `true`.
283
+ * @property {boolean} [fullscreen.exitOnRotate]
284
+ * If the device is rotated, exit fullscreen, unless `lockOnRotate` is used.
285
+ * Default `true`.
286
+ * @property {boolean} [fullscreen.lockOnRotate]
287
+ * When going fullscreen in response to rotation (`enterOnRotate`), also lock the orientation (not supported by iOS).
288
+ * Default `true`.
289
+ * @property {boolean} [fullscreen.lockToLandscapeOnEnter]
290
+ * When fullscreen is entered by any means, lock the orientation (not supported by iOS).
291
+ * Default `false`.
292
+ * @property {boolean} [fullscreen.swipeToFullscreen]
293
+ * Swipe up to enter fullscreen.
294
+ * Default `false`.
295
+ * @property {boolean} [fullscreen.swipeFromFullscreen]
296
+ * Swipe down to exit fullscreen.
297
+ * Won't do anything on iOS native fullscreen, which has its own swipe down exit gesture.
298
+ * Default `false`.
299
+ * @property {boolean} [fullscreen.disabled]
300
+ * All fullscreen functionality provided by this plugin disabled.
301
+ * Default `false`.
302
+ * @property {Object} [touchControls]
303
+ * Options for tap overlay.
304
+ * @property {number} [touchControls.seekSeconds]
305
+ * Increment to seek in seconds.
306
+ * Default `10`.
307
+ * @property {number} [touchControls.tapTimeout]
308
+ * Timeout to consider multiple taps as double rather than two single.
309
+ * Default `300`.
310
+ * @property {boolean} [touchControls.disableOnEnd]
311
+ * Disable the touch overlay when the video ends.
312
+ * Useful if an end screen overlay is used to avoid conflict.
313
+ * Default `false`.
314
+ * @property {boolean} [touchControls.disabled]
315
+ * All tap overlay functionality provided by this plugin disabled.
316
+ * Default `false`.
317
+ * @internal
318
+ * @property {boolean} [forceForTesting]
319
+ * Used in unit tests
320
+ */
321
+
322
+ /** @type {MobileUiOptions} */
149
323
  const defaults = {
150
324
  fullscreen: {
151
325
  enterOnRotate: true,
152
326
  exitOnRotate: true,
153
327
  lockOnRotate: true,
154
328
  lockToLandscapeOnEnter: false,
329
+ swipeToFullscreen: false,
330
+ swipeFromFullscreen: false,
155
331
  disabled: false
156
332
  },
157
333
  touchControls: {
@@ -194,7 +370,7 @@ const getOrientation = () => {
194
370
  * @param {Player} player
195
371
  * A Video.js player object.
196
372
  *
197
- * @param {Object} [options={}]
373
+ * @param {MobileUiOptions} [options={}]
198
374
  * A plain object containing options for the plugin.
199
375
  */
200
376
  const onPlayerReady = (player, options) => {
@@ -211,17 +387,22 @@ const onPlayerReady = (player, options) => {
211
387
  if (options.fullscreen.disabled) {
212
388
  return;
213
389
  }
390
+ if (options.fullscreen.swipeToFullscreen || options.fullscreen.swipeFromFullscreen) {
391
+ initSwipe(player, options);
392
+ }
214
393
  let locked = false;
215
394
  const rotationHandler = () => {
216
395
  const currentOrientation = getOrientation();
217
396
  if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
218
- if (player.paused() === false) {
219
- player.requestFullscreen();
397
+ if (!player.paused() && !player.isFullscreen()) {
398
+ player.requestFullscreen().catch(err => {
399
+ player.log.warn('Browser refused fullscreen request:', err);
400
+ });
220
401
  if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) && screen.orientation && screen.orientation.lock) {
221
402
  screen.orientation.lock('landscape').then(() => {
222
403
  locked = true;
223
- }).catch(e => {
224
- videojs__default["default"].log('Browser refused orientation lock:', e);
404
+ }).catch(err => {
405
+ videojs__default["default"].log.warn('Browser refused orientation lock:', err);
225
406
  });
226
407
  }
227
408
  }
@@ -246,6 +427,7 @@ const onPlayerReady = (player, options) => {
246
427
  }
247
428
  }
248
429
  player.on('fullscreenchange', _ => {
430
+ player.log('fullscreenchange', player.isFullscreen(), options.fullscreen.lockToLandscapeOnEnter, getOrientation());
249
431
  if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') {
250
432
  screen.orientation.lock('landscape').then(() => {
251
433
  locked = true;
@@ -266,40 +448,10 @@ const onPlayerReady = (player, options) => {
266
448
  };
267
449
 
268
450
  /**
269
- * A video.js plugin.
270
- *
271
- * Adds a monile UI for player control, and fullscreen orientation control
451
+ * Adds a mobile UI for player control, and fullscreen orientation control
272
452
  *
273
453
  * @function mobileUi
274
- * @param {Object} [options={}]
275
- * Plugin options.
276
- * @param {boolean} [options.forceForTesting=false]
277
- * Enables the display regardless of user agent, for testing purposes
278
- * @param {Object} [options.fullscreen={}]
279
- * Fullscreen options.
280
- * @param {boolean} [options.fullscreen.disabled=false]
281
- * If true no fullscreen handling except the *deprecated* iOS fullwindow hack
282
- * @param {boolean} [options.fullscreen.enterOnRotate=true]
283
- * Whether to go fullscreen when rotating to landscape
284
- * @param {boolean} [options.fullscreen.exitOnRotate=true]
285
- * Whether to leave fullscreen when rotating to portrait (if not locked)
286
- * @param {boolean} [options.fullscreen.lockOnRotate=true]
287
- * Whether to lock orientation when rotating to landscape
288
- * Unlocked when exiting fullscreen or on 'ended
289
- * @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false]
290
- * Whether to always lock orientation to landscape on fullscreen mode
291
- * Unlocked when exiting fullscreen or on 'ended'
292
- * @param {Object} [options.touchControls={}]
293
- * Touch UI options.
294
- * @param {boolean} [options.touchControls.disabled=false]
295
- * If true no touch controls are added.
296
- * @param {int} [options.touchControls.seekSeconds=10]
297
- * Number of seconds to seek on double-tap
298
- * @param {int} [options.touchControls.tapTimeout=300]
299
- * Interval in ms to be considered a doubletap
300
- * @param {boolean} [options.touchControls.disableOnEnd=false]
301
- * Whether to disable when the video ends (e.g., if there is an endscreen)
302
- * Never shows if the endscreen plugin is present
454
+ * @param {MobileUiOptions} [options={}] Plugin options
303
455
  */
304
456
  const mobileUi = function (options = {}) {
305
457
  if (options.forceForTesting || videojs__default["default"].browser.IS_ANDROID || videojs__default["default"].browser.IS_IOS) {
@@ -1,2 +1,104 @@
1
- /*! @name videojs-mobile-ui @version 1.2.0-alpha.0 @license MIT */
2
- @keyframes fadeAndScale{0%,to{opacity:0}25%{opacity:1}}.video-js.vjs-mobile-ui.vjs-has-started:not(.vjs-ad-playing) .vjs-touch-overlay{position:absolute;pointer-events:auto;top:0}.video-js.vjs-mobile-ui .vjs-touch-overlay{display:block;width:100%;height:100%;pointer-events:none}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip{opacity:0;animation:fadeAndScale .8s linear;background-repeat:no-repeat;background-position:80% center;background-size:10%;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip:after{content:attr(data-skip-text);position:absolute;top:60%;left:70%}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip.reverse{background-position:20% center;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip.reverse:after{right:70%;left:unset}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;width:30%;height:80%;pointer-events:none;opacity:0;transition:opacity .3s ease}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control .vjs-icon-placeholder::before{content:'';background-size:60%;background-position:center center;background-repeat:no-repeat;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control.vjs-paused .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control.vjs-ended .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.show-play-toggle .vjs-play-control{opacity:1;pointer-events:auto}.video-js.vjs-mobile-ui.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay{display:none}
1
+ /**
2
+ * css for videojs-mobile-ui
3
+ */
4
+
5
+ @keyframes fadeAndScale {
6
+ 0% {
7
+ opacity: 0
8
+ }
9
+ 25% {
10
+ opacity: 1;
11
+ }
12
+ 100% {
13
+ opacity: 0;
14
+ }
15
+ }
16
+
17
+ .video-js.vjs-mobile-ui {
18
+
19
+ &.vjs-has-started:not(.vjs-ad-playing) .vjs-touch-overlay {
20
+ position: absolute;
21
+ pointer-events: auto;
22
+ top: 0;
23
+ }
24
+
25
+ .vjs-touch-overlay {
26
+ display: block;
27
+ width: 100%;
28
+ height: 100%;
29
+ pointer-events: none;
30
+
31
+ &.skip {
32
+ opacity: 0;
33
+ animation: fadeAndScale 0.8s linear;
34
+ background-repeat: no-repeat;
35
+ background-position: 80% center;
36
+ background-size: 10%;
37
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
38
+
39
+ &:after {
40
+ content: attr(data-skip-text);
41
+ position: absolute;
42
+ top: 60%;
43
+ left: 70%;
44
+ }
45
+ }
46
+
47
+ &.skip.reverse {
48
+ background-position: 20% center;
49
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
50
+
51
+ &:after {
52
+ right: 70%;
53
+ left: unset;
54
+ }
55
+ }
56
+
57
+ .vjs-play-control {
58
+ top: 50%;
59
+ left: 50%;
60
+ transform: translate(-50%, -50%);
61
+ position: absolute;
62
+ width: 30%;
63
+ height: 80%;
64
+ pointer-events: none;
65
+ opacity: 0;
66
+ transition: opacity 0.3s ease;
67
+
68
+ .vjs-icon-placeholder::before {
69
+ content: '';
70
+ background-size: 60%;
71
+ background-position: center center;
72
+ background-repeat: no-repeat;
73
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
74
+ }
75
+
76
+ &.vjs-paused .vjs-icon-placeholder::before {
77
+ content: '';
78
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
79
+ }
80
+
81
+ &.vjs-ended .vjs-icon-placeholder::before {
82
+ content: '';
83
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>');
84
+ }
85
+ }
86
+
87
+ &.show-play-toggle .vjs-play-control {
88
+ opacity: 1;
89
+ pointer-events: auto;
90
+ }
91
+
92
+ }
93
+
94
+ &.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay {
95
+ display: none;
96
+ }
97
+
98
+ &:not(:fullscreen).using-fs-swipe-up,
99
+ &:fullscreen.using-fs-swipe-down {
100
+ touch-action: none;
101
+ overflow: hidden;
102
+ }
103
+
104
+ }