videojs-mobile-ui 1.2.0-alpha.0 → 1.2.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/README.md +104 -53
- package/dist/lang/de.js +1 -3
- package/dist/lang/en.js +1 -3
- package/dist/lang/it.js +1 -3
- package/dist/videojs-mobile-ui.cjs.js +195 -43
- package/dist/videojs-mobile-ui.css +104 -2
- package/dist/videojs-mobile-ui.es.js +195 -43
- package/dist/videojs-mobile-ui.js +204 -53
- package/dist/videojs-mobile-ui.min.js +2 -2
- package/docs/api/TouchOverlay.html +772 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/api/global.html +1485 -0
- package/docs/api/index.html +159 -0
- package/docs/api/plugin.js.html +266 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/styles/jsdoc-default.css +358 -0
- package/docs/api/styles/prettify-jsdoc.css +111 -0
- package/docs/api/styles/prettify-tomorrow.css +132 -0
- package/docs/api/swipeFullscreen.js.html +173 -0
- package/docs/api/touchOverlay.js.html +203 -0
- package/index.html +238 -170
- package/package.json +23 -13
- package/scripts/lang.js +24 -0
- package/scripts/netlify.js +16 -0
- package/scripts/postcss.config.js +29 -5
- package/scripts/readme-options.js +370 -0
- package/scripts/rollup.config.js +0 -8
- package/src/plugin.css +6 -0
- package/src/plugin.js +65 -39
- package/src/swipeFullscreen.js +122 -0
- package/src/touchOverlay.js +7 -3
- package/test/plugin.test.js +125 -18
- package/test/swipeFullscreen.test.js +365 -0
package/README.md
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
# videojs-mobile-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A more native mobile user experience for Video.js.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9
|
+
### 👆 Touch Controls
|
|
12
10
|
|
|
13
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
21
|
-
## Installation
|
|
15
|
+
### 📱 Fullscreen Orientation:
|
|
22
16
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
### 🚀 Swipe Gestures (Optional)
|
|
42
22
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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 <git@misterben.me>
|
|
151
201
|
|
|
152
|
-
|
|
153
|
-
[
|
|
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
package/dist/lang/en.js
CHANGED
package/dist/lang/it.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
/*! @name videojs-mobile-ui @version 1.2.0
|
|
1
|
+
/*! @name videojs-mobile-ui @version 1.2.0 @license MIT */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var videojs = require('video.js');
|
|
5
|
-
var window = require('
|
|
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
|
|
12
|
+
var version = "1.2.0";
|
|
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 {
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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()
|
|
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(
|
|
224
|
-
videojs__default["default"].log('Browser refused orientation lock:',
|
|
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
|
-
*
|
|
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 {
|
|
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
|
-
|
|
2
|
-
|
|
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
|
+
}
|