videojs-mobile-ui 0.8.0 → 0.9.0-beta.3
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/CHANGELOG.md +0 -31
- package/README.md +110 -50
- package/dist/videojs-mobile-ui.cjs.js +265 -222
- package/dist/videojs-mobile-ui.css +2 -2
- package/dist/videojs-mobile-ui.es.js +259 -215
- package/dist/videojs-mobile-ui.js +268 -264
- package/dist/videojs-mobile-ui.min.js +2 -2
- package/docs/api/swipeFullscreen.js.html +173 -0
- package/index.html +238 -176
- package/package.json +15 -15
- package/src/plugin.css +21 -3
- package/src/plugin.js +17 -54
- package/src/swipeFullscreen.js +117 -0
- package/src/touchOverlay.js +62 -56
- package/test/plugin.test.js +128 -20
- package/test/swipeFullscreen.test.js +365 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videojs-mobile-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-beta.3",
|
|
4
4
|
"description": "Mobile tap controls and fullscreen on rotate for Video.js",
|
|
5
5
|
"main": "dist/videojs-mobile-ui.cjs.js",
|
|
6
6
|
"module": "dist/videojs-mobile-ui.es.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"watch": "npm-run-all -p watch:*",
|
|
32
32
|
"watch:css": "npm run build:css -- -w",
|
|
33
33
|
"watch:js": "npm run build:js -- -w",
|
|
34
|
-
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose"
|
|
34
|
+
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose --skip-es-check"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=14",
|
|
@@ -74,21 +74,21 @@
|
|
|
74
74
|
"video.js": "^6 || ^7"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@babel/runtime": "^7.
|
|
78
|
-
"@videojs/generator-helpers": "~2.0
|
|
79
|
-
"husky": "^
|
|
80
|
-
"jsdoc": "^
|
|
81
|
-
"karma": "^6.
|
|
82
|
-
"postcss": "^8.
|
|
77
|
+
"@babel/runtime": "^7.28.6",
|
|
78
|
+
"@videojs/generator-helpers": "~3.2.0",
|
|
79
|
+
"husky": "^8.0.3",
|
|
80
|
+
"jsdoc": "^4.0.5",
|
|
81
|
+
"karma": "^6.4.4",
|
|
82
|
+
"postcss": "^8.5.6",
|
|
83
83
|
"postcss-cli": "^8.3.1",
|
|
84
|
-
"rollup": "^2.
|
|
85
|
-
"sinon": "^
|
|
86
|
-
"video.js": "^
|
|
84
|
+
"rollup": "^2.79.2",
|
|
85
|
+
"sinon": "^14.0.1",
|
|
86
|
+
"video.js": "^7.21.7",
|
|
87
87
|
"videojs-generate-karma-config": "~8.0.0",
|
|
88
|
-
"videojs-generate-postcss-config": "
|
|
89
|
-
"videojs-generate-rollup-config": "
|
|
90
|
-
"videojs-generator-verify": "^4.1.
|
|
88
|
+
"videojs-generate-postcss-config": "^3.0.1",
|
|
89
|
+
"videojs-generate-rollup-config": "^7.0.2",
|
|
90
|
+
"videojs-generator-verify": "^4.1.3",
|
|
91
91
|
"videojs-languages": "^2.0.0",
|
|
92
|
-
"videojs-standard": "^
|
|
92
|
+
"videojs-standard": "^9.1.0"
|
|
93
93
|
}
|
|
94
94
|
}
|
package/src/plugin.css
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
.video-js {
|
|
17
|
+
.video-js.vjs-mobile-ui {
|
|
18
18
|
|
|
19
|
-
&.vjs-has-started .vjs-touch-overlay {
|
|
19
|
+
&.vjs-has-started:not(.vjs-ad-playing) .vjs-touch-overlay {
|
|
20
20
|
position: absolute;
|
|
21
21
|
pointer-events: auto;
|
|
22
22
|
top: 0;
|
|
@@ -30,16 +30,28 @@
|
|
|
30
30
|
|
|
31
31
|
&.skip {
|
|
32
32
|
opacity: 0;
|
|
33
|
-
animation: fadeAndScale 0.
|
|
33
|
+
animation: fadeAndScale 0.8s linear;
|
|
34
34
|
background-repeat: no-repeat;
|
|
35
35
|
background-position: 80% center;
|
|
36
36
|
background-size: 10%;
|
|
37
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
|
+
}
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
&.skip.reverse {
|
|
41
48
|
background-position: 20% center;
|
|
42
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
|
+
}
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
.vjs-play-control {
|
|
@@ -83,4 +95,10 @@
|
|
|
83
95
|
display: none;
|
|
84
96
|
}
|
|
85
97
|
|
|
98
|
+
&:not(:fullscreen).using-fs-swipe-up,
|
|
99
|
+
&:fullscreen.using-fs-swipe-down {
|
|
100
|
+
touch-action: none;
|
|
101
|
+
overflow: hidden;
|
|
102
|
+
}
|
|
103
|
+
|
|
86
104
|
}
|
package/src/plugin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import videojs from 'video.js';
|
|
2
2
|
import {version as VERSION} from '../package.json';
|
|
3
3
|
import './touchOverlay.js';
|
|
4
|
+
import initSwipe from './swipeFullscreen.js';
|
|
4
5
|
import window from 'global/window';
|
|
5
6
|
|
|
6
7
|
// Default options for the plugin.
|
|
@@ -10,7 +11,8 @@ const defaults = {
|
|
|
10
11
|
exitOnRotate: true,
|
|
11
12
|
lockOnRotate: true,
|
|
12
13
|
lockToLandscapeOnEnter: false,
|
|
13
|
-
|
|
14
|
+
swipeToFullscreen: false,
|
|
15
|
+
swipeFromFullscreen: false,
|
|
14
16
|
disabled: false
|
|
15
17
|
},
|
|
16
18
|
touchControls: {
|
|
@@ -22,6 +24,7 @@ const defaults = {
|
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
const screen = window.screen;
|
|
27
|
+
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* Gets 'portrait' or 'lanscape' from the two orientation APIs
|
|
@@ -49,9 +52,6 @@ const getOrientation = () => {
|
|
|
49
52
|
return 'portrait';
|
|
50
53
|
};
|
|
51
54
|
|
|
52
|
-
// Cross-compatibility for Video.js 5 and 6.
|
|
53
|
-
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
54
|
-
|
|
55
55
|
/**
|
|
56
56
|
* Add UI and event listeners
|
|
57
57
|
*
|
|
@@ -59,23 +59,12 @@ const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
|
59
59
|
* @param {Player} player
|
|
60
60
|
* A Video.js player object.
|
|
61
61
|
*
|
|
62
|
-
* @param {
|
|
62
|
+
* @param {MobileUiOptions} [options={}]
|
|
63
63
|
* A plain object containing options for the plugin.
|
|
64
64
|
*/
|
|
65
65
|
const onPlayerReady = (player, options) => {
|
|
66
66
|
player.addClass('vjs-mobile-ui');
|
|
67
67
|
|
|
68
|
-
if (options.fullscreen.iOS) {
|
|
69
|
-
videojs.log.warn('videojs-mobile-ui: `fullscreen.iOS` is deprecated. Use Video.js option `preferFullWindow` instead.');
|
|
70
|
-
if (videojs.browser.IS_IOS && videojs.browser.IOS_VERSION > 9 &&
|
|
71
|
-
!player.el_.ownerDocument.querySelector('.bc-iframe')) {
|
|
72
|
-
player.tech_.el_.setAttribute('playsinline', 'playsinline');
|
|
73
|
-
player.tech_.supportsFullScreen = function() {
|
|
74
|
-
return false;
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
68
|
if (!options.touchControls.disabled) {
|
|
80
69
|
|
|
81
70
|
if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') {
|
|
@@ -105,20 +94,26 @@ const onPlayerReady = (player, options) => {
|
|
|
105
94
|
return;
|
|
106
95
|
}
|
|
107
96
|
|
|
97
|
+
if (options.fullscreen.swipeToFullscreen || options.fullscreen.swipeFromFullscreen) {
|
|
98
|
+
initSwipe(player, options);
|
|
99
|
+
}
|
|
100
|
+
|
|
108
101
|
let locked = false;
|
|
109
102
|
|
|
110
103
|
const rotationHandler = () => {
|
|
111
104
|
const currentOrientation = getOrientation();
|
|
112
105
|
|
|
113
106
|
if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
|
|
114
|
-
if (player.paused()
|
|
115
|
-
player.requestFullscreen()
|
|
107
|
+
if (!player.paused() && !player.isFullscreen()) {
|
|
108
|
+
player.requestFullscreen().catch((err) => {
|
|
109
|
+
player.log.warn('Browser refused fullscreen request:', err);
|
|
110
|
+
});
|
|
116
111
|
if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) &&
|
|
117
112
|
screen.orientation && screen.orientation.lock) {
|
|
118
113
|
screen.orientation.lock('landscape').then(() => {
|
|
119
114
|
locked = true;
|
|
120
|
-
}).catch((
|
|
121
|
-
videojs.log('Browser refused orientation lock:',
|
|
115
|
+
}).catch((err) => {
|
|
116
|
+
videojs.log.warn('Browser refused orientation lock:', err);
|
|
122
117
|
});
|
|
123
118
|
}
|
|
124
119
|
}
|
|
@@ -168,42 +163,10 @@ const onPlayerReady = (player, options) => {
|
|
|
168
163
|
};
|
|
169
164
|
|
|
170
165
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* Adds a monile UI for player control, and fullscreen orientation control
|
|
166
|
+
* Adds a mobile UI for player control, and fullscreen orientation control
|
|
174
167
|
*
|
|
175
168
|
* @function mobileUi
|
|
176
|
-
* @param {Object} [options={}]
|
|
177
|
-
* Plugin options.
|
|
178
|
-
* @param {boolean} [options.forceForTesting=false]
|
|
179
|
-
* Enables the display regardless of user agent, for testing purposes
|
|
180
|
-
* @param {Object} [options.fullscreen={}]
|
|
181
|
-
* Fullscreen options.
|
|
182
|
-
* @param {boolean} [options.fullscreen.disabled=false]
|
|
183
|
-
* If true no fullscreen handling except the *deprecated* iOS fullwindow hack
|
|
184
|
-
* @param {boolean} [options.fullscreen.enterOnRotate=true]
|
|
185
|
-
* Whether to go fullscreen when rotating to landscape
|
|
186
|
-
* @param {boolean} [options.fullscreen.exitOnRotate=true]
|
|
187
|
-
* Whether to leave fullscreen when rotating to portrait (if not locked)
|
|
188
|
-
* @param {boolean} [options.fullscreen.lockOnRotate=true]
|
|
189
|
-
* Whether to lock orientation when rotating to landscape
|
|
190
|
-
* Unlocked when exiting fullscreen or on 'ended
|
|
191
|
-
* @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false]
|
|
192
|
-
* Whether to always lock orientation to landscape on fullscreen mode
|
|
193
|
-
* Unlocked when exiting fullscreen or on 'ended'
|
|
194
|
-
* @param {boolean} [options.fullscreen.iOS=false]
|
|
195
|
-
* Deprecated: Whether to disable iOS's native fullscreen so controls can work
|
|
196
|
-
* @param {Object} [options.touchControls={}]
|
|
197
|
-
* Touch UI options.
|
|
198
|
-
* @param {boolean} [options.touchControls.disabled=false]
|
|
199
|
-
* If true no touch controls are added.
|
|
200
|
-
* @param {int} [options.touchControls.seekSeconds=10]
|
|
201
|
-
* Number of seconds to seek on double-tap
|
|
202
|
-
* @param {int} [options.touchControls.tapTimeout=300]
|
|
203
|
-
* Interval in ms to be considered a doubletap
|
|
204
|
-
* @param {boolean} [options.touchControls.disableOnEnd=false]
|
|
205
|
-
* Whether to disable when the video ends (e.g., if there is an endscreen)
|
|
206
|
-
* Never shows if the endscreen plugin is present
|
|
169
|
+
* @param {Object} [options={}] Plugin options
|
|
207
170
|
*/
|
|
208
171
|
const mobileUi = function(options = {}) {
|
|
209
172
|
if (options.forceForTesting || videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets up swiping to enter and exit fullscreen.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} player
|
|
5
|
+
* The player to initialise on.
|
|
6
|
+
* @param {Object} pluginOptions
|
|
7
|
+
* The options used by the mobile ui plugin.
|
|
8
|
+
*/
|
|
9
|
+
const initSwipe = (player, pluginOptions) => {
|
|
10
|
+
const {swipeToFullscreen, swipeFromFullscreen} = pluginOptions.fullscreen;
|
|
11
|
+
|
|
12
|
+
if (swipeToFullscreen) {
|
|
13
|
+
player.addClass('using-fs-swipe-up');
|
|
14
|
+
}
|
|
15
|
+
if (swipeFromFullscreen) {
|
|
16
|
+
player.addClass('using-fs-swipe-down');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let touchStartY = 0;
|
|
20
|
+
let couldBeSwiping = false;
|
|
21
|
+
const swipeThreshold = 30;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Monitor the possible start of a swipe
|
|
25
|
+
*
|
|
26
|
+
* @param {TouchEvent} e Triggering touch event
|
|
27
|
+
*/
|
|
28
|
+
const onStart = (e) => {
|
|
29
|
+
const isFullscreen = player.isFullscreen();
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
(!isFullscreen && !swipeToFullscreen) ||
|
|
33
|
+
(isFullscreen && !swipeFromFullscreen)
|
|
34
|
+
) {
|
|
35
|
+
couldBeSwiping = false;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
touchStartY = e.changedTouches[0].clientY;
|
|
40
|
+
couldBeSwiping = true;
|
|
41
|
+
player.tech_.el().style.transition = '';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Monitor the movement of a swipe
|
|
46
|
+
*
|
|
47
|
+
* @param {TouchEvent} e Triggering touch event
|
|
48
|
+
*/
|
|
49
|
+
const onMove = (e) => {
|
|
50
|
+
if (!couldBeSwiping) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const currentY = e.touches[0].clientY;
|
|
55
|
+
const deltaY = touchStartY - currentY;
|
|
56
|
+
const isFullscreen = player.isFullscreen();
|
|
57
|
+
|
|
58
|
+
let scale = 1;
|
|
59
|
+
|
|
60
|
+
if (!isFullscreen && deltaY > 0) {
|
|
61
|
+
// Swiping up to enter fullscreen: Zoom in (Max 1.1)
|
|
62
|
+
scale = 1 + Math.min(0.1, deltaY / 500);
|
|
63
|
+
player.tech_.el().style.transform = `scale(${scale})`;
|
|
64
|
+
} else if (isFullscreen && deltaY < 0) {
|
|
65
|
+
// Swiping down to exit fullscreen: Zoom out (Min 0.9)
|
|
66
|
+
scale = 1 - Math.min(0.1, Math.abs(deltaY) / 500);
|
|
67
|
+
player.tech_.el().style.transform = `scale(${scale})`;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Monitor the touch end to determine a valid swipe
|
|
73
|
+
*
|
|
74
|
+
* @param {TouchEvent} e Triggering touch event
|
|
75
|
+
*/
|
|
76
|
+
const onEnd = (e) => {
|
|
77
|
+
if (!couldBeSwiping) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
couldBeSwiping = false;
|
|
81
|
+
|
|
82
|
+
player.tech_.el().style.transition = 'transform 0.3s ease-out';
|
|
83
|
+
player.tech_.el().style.transform = 'scale(1)';
|
|
84
|
+
|
|
85
|
+
if (e.type === 'touchcancel') {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const touchEndY = e.changedTouches[0].clientY;
|
|
90
|
+
const deltaY = touchStartY - touchEndY;
|
|
91
|
+
|
|
92
|
+
if (deltaY > swipeThreshold && !player.isFullscreen()) {
|
|
93
|
+
player.requestFullscreen().catch((err) => {
|
|
94
|
+
player.log.warn('Browser refused fullscreen', err);
|
|
95
|
+
});
|
|
96
|
+
} else if (deltaY < -swipeThreshold && player.isFullscreen()) {
|
|
97
|
+
player.exitFullscreen();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
player.el().addEventListener('touchstart', onStart, { passive: true });
|
|
102
|
+
player.el().addEventListener('touchmove', onMove, { passive: true });
|
|
103
|
+
player.el().addEventListener('touchend', onEnd, { passive: true });
|
|
104
|
+
player.el().addEventListener('touchcancel', onEnd, { passive: true });
|
|
105
|
+
|
|
106
|
+
player.on('dispose', () => {
|
|
107
|
+
player.el().removeEventListener('touchstart', onStart, { passive: true });
|
|
108
|
+
player.el().removeEventListener('touchmove', onMove, { passive: true });
|
|
109
|
+
player.el().removeEventListener('touchend', onEnd, { passive: true });
|
|
110
|
+
player.el().removeEventListener('touchcancel', onEnd, { passive: true });
|
|
111
|
+
player.tech_.el().style.transform = '';
|
|
112
|
+
player.tech_.el().style.transition = '';
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default initSwipe;
|
package/src/touchOverlay.js
CHANGED
|
@@ -6,9 +6,23 @@
|
|
|
6
6
|
import videojs from 'video.js';
|
|
7
7
|
import window from 'global/window';
|
|
8
8
|
|
|
9
|
+
/** @import Player from 'video.js/dist/types/player' */
|
|
10
|
+
|
|
9
11
|
const Component = videojs.getComponent('Component');
|
|
10
12
|
const dom = videojs.dom || videojs;
|
|
11
13
|
|
|
14
|
+
const debounce = (callback, wait) => {
|
|
15
|
+
let timeoutId = null;
|
|
16
|
+
|
|
17
|
+
return (...args) => {
|
|
18
|
+
window.clearTimeout(timeoutId);
|
|
19
|
+
|
|
20
|
+
timeoutId = window.setTimeout(() => {
|
|
21
|
+
callback.apply(null, args);
|
|
22
|
+
}, wait);
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
12
26
|
/**
|
|
13
27
|
* The `TouchOverlay` is an overlay to capture tap events.
|
|
14
28
|
*
|
|
@@ -30,6 +44,7 @@ class TouchOverlay extends Component {
|
|
|
30
44
|
|
|
31
45
|
this.seekSeconds = options.seekSeconds;
|
|
32
46
|
this.tapTimeout = options.tapTimeout;
|
|
47
|
+
this.taps = 0;
|
|
33
48
|
|
|
34
49
|
// Add play toggle overlay
|
|
35
50
|
this.addChild('playToggle', {});
|
|
@@ -44,6 +59,46 @@ class TouchOverlay extends Component {
|
|
|
44
59
|
this.player_.options_.inactivityTimeout = 5000;
|
|
45
60
|
}
|
|
46
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Debounced tap handler.
|
|
64
|
+
* Seeks number of (taps - 1) * configured seconds to skip.
|
|
65
|
+
* One tap is a non-op
|
|
66
|
+
*
|
|
67
|
+
* @param {Event} event
|
|
68
|
+
*/
|
|
69
|
+
this.handleTaps_ = debounce(event => {
|
|
70
|
+
const increment = (this.taps - 1) * this.seekSeconds;
|
|
71
|
+
|
|
72
|
+
this.taps = 0;
|
|
73
|
+
if (increment < 1) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const rect = this.el_.getBoundingClientRect();
|
|
78
|
+
const x = event.changedTouches[0].clientX - rect.left;
|
|
79
|
+
|
|
80
|
+
// Check if double tap is in left or right area
|
|
81
|
+
if (x < rect.width * 0.4) {
|
|
82
|
+
this.player_.currentTime(Math.max(0, this.player_.currentTime() - increment));
|
|
83
|
+
this.addClass('reverse');
|
|
84
|
+
} else if (x > rect.width - (rect.width * 0.4)) {
|
|
85
|
+
this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + increment));
|
|
86
|
+
this.removeClass('reverse');
|
|
87
|
+
} else {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Remove play toggle if showing
|
|
92
|
+
this.removeClass('show-play-toggle');
|
|
93
|
+
|
|
94
|
+
// Remove and readd class to trigger animation
|
|
95
|
+
this.setAttribute('data-skip-text', `${increment} ${this.localize('seconds')}`);
|
|
96
|
+
this.removeClass('skip');
|
|
97
|
+
window.requestAnimationFrame(() => {
|
|
98
|
+
this.addClass('skip');
|
|
99
|
+
});
|
|
100
|
+
}, this.tapTimeout);
|
|
101
|
+
|
|
47
102
|
this.enable();
|
|
48
103
|
}
|
|
49
104
|
|
|
@@ -76,65 +131,16 @@ class TouchOverlay extends Component {
|
|
|
76
131
|
return;
|
|
77
132
|
}
|
|
78
133
|
|
|
79
|
-
event.
|
|
80
|
-
|
|
81
|
-
if (this.firstTapCaptured) {
|
|
82
|
-
this.firstTapCaptured = false;
|
|
83
|
-
if (this.timeout) {
|
|
84
|
-
window.clearTimeout(this.timeout);
|
|
85
|
-
}
|
|
86
|
-
this.handleDoubleTap(event);
|
|
87
|
-
} else {
|
|
88
|
-
this.firstTapCaptured = true;
|
|
89
|
-
this.timeout = window.setTimeout(() => {
|
|
90
|
-
this.firstTapCaptured = false;
|
|
91
|
-
this.handleSingleTap(event);
|
|
92
|
-
}, this.tapTimeout);
|
|
134
|
+
if (event.cancelable) {
|
|
135
|
+
event.preventDefault();
|
|
93
136
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Toggles display of play toggle
|
|
98
|
-
*
|
|
99
|
-
* @param {Event} event
|
|
100
|
-
* The touch event
|
|
101
|
-
*
|
|
102
|
-
*/
|
|
103
|
-
handleSingleTap(event) {
|
|
104
|
-
this.removeClass('skip');
|
|
105
|
-
this.toggleClass('show-play-toggle');
|
|
106
|
-
}
|
|
107
137
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
* The touch event
|
|
113
|
-
*
|
|
114
|
-
*/
|
|
115
|
-
handleDoubleTap(event) {
|
|
116
|
-
const rect = this.el_.getBoundingClientRect();
|
|
117
|
-
const x = event.changedTouches[0].clientX - rect.left;
|
|
118
|
-
|
|
119
|
-
// Check if double tap is in left or right area
|
|
120
|
-
if (x < rect.width * 0.4) {
|
|
121
|
-
this.player_.currentTime(Math.max(0, this.player_.currentTime() - this.seekSeconds));
|
|
122
|
-
this.addClass('reverse');
|
|
123
|
-
} else if (x > rect.width - (rect.width * 0.4)) {
|
|
124
|
-
this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + this.seekSeconds));
|
|
125
|
-
this.removeClass('reverse');
|
|
126
|
-
} else {
|
|
127
|
-
return;
|
|
138
|
+
this.taps += 1;
|
|
139
|
+
if (this.taps === 1) {
|
|
140
|
+
this.removeClass('skip');
|
|
141
|
+
this.toggleClass('show-play-toggle');
|
|
128
142
|
}
|
|
129
|
-
|
|
130
|
-
// Remove play toggle if showing
|
|
131
|
-
this.removeClass('show-play-toggle');
|
|
132
|
-
|
|
133
|
-
// Remove and readd class to trigger animation
|
|
134
|
-
this.removeClass('skip');
|
|
135
|
-
window.requestAnimationFrame(() => {
|
|
136
|
-
this.addClass('skip');
|
|
137
|
-
});
|
|
143
|
+
this.handleTaps_(event);
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
/**
|