Skip to content

Commit 3971eda

Browse files
committed
feat(esl-media): play-in-viewport feature reworked to be more customizable
- `ESLMedia.prototype.RATIO_TO_ACTIVATE` introduced to control min ratio to init lazy players - `ESLMedia.prototype.RATIO_TO_STOP` introduced to control ratio to stop video (play-in-viewport feature) - `ESLMedia.prototype.RATIO_TO_PLAY` introduced to control ratio to restart video (play-in-viewport feature)
1 parent 2f972eb commit 3971eda

File tree

5 files changed

+36
-16
lines changed

5 files changed

+36
-16
lines changed
Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import type {ESLMedia} from './esl-media';
22

3-
const RATIO_TO_PLAY = 0.5; // TODO: customizable, at least global
4-
const RATIO_TO_STOP = 0.20; // TODO: customizable, at least global
5-
6-
const RATIO_TO_ACTIVATE = 0.05;
7-
83
let iObserver: IntersectionObserver;
94

105
/** ESL Media Play-In-Viewport IntersectionObserver instance */
@@ -13,24 +8,40 @@ export function getIObserver(lazy: boolean = false): IntersectionObserver {
138
iObserver = new IntersectionObserver(function (entries) {
149
entries.forEach(handleViewport);
1510
}, {
16-
threshold: [RATIO_TO_STOP, RATIO_TO_PLAY]
11+
threshold: [0.05, 0.1, 0.25, 0.33, 0.5, 0.66, 0.75, 0.9, 1]
1712
});
1813
}
1914
return iObserver;
2015
}
2116

2217
function handleViewport(entry: IntersectionObserverEntry): void {
2318
const video = entry.target as ESLMedia;
19+
const root = entry.rootBounds || {
20+
width: Number.POSITIVE_INFINITY,
21+
height: Number.POSITIVE_INFINITY
22+
};
23+
const rect = entry.intersectionRect;
24+
const ratio = Math.max(
25+
(rect.width * rect.height) / (root.width * root.height),
26+
entry.intersectionRatio
27+
);
28+
2429
// Removes `lazy` attribute when media is in the viewport with min ratio RATIO_TO_ACTIVATE
25-
if (entry.isIntersecting && entry.intersectionRatio >= RATIO_TO_ACTIVATE && video.lazy === 'auto') {
30+
if (entry.isIntersecting && ratio >= video.RATIO_TO_ACTIVATE && video.lazy === 'auto') {
2631
video.$$attr('lazy', false);
2732
}
28-
// Videos that playing and out of min ratio RATIO_TO_STOP should be stopped
29-
if (video.active && entry.intersectionRatio <= RATIO_TO_STOP) {
33+
34+
// Skip if video state management if feature is disabled
35+
if (!video.playInViewport) return;
36+
37+
// Videos that playing and out of min ratio should be stopped
38+
if (video.active && ratio <= video.RATIO_TO_STOP) {
3039
video.pause(true);
3140
}
32-
// Play should start only for autoplay-able videos that are visible enough and was not manually stopped
33-
if (!video.isUserInitiated && video.autoplay && entry.intersectionRatio >= RATIO_TO_PLAY) {
41+
// Videos that not playing and in min ratio to start should be started
42+
if (!video.active && ratio >= video.RATIO_TO_PLAY) {
43+
// Disallow for non autoplay-able videos or videos that was controlled by user
44+
if (!video.autoplay || video.isUserInitiated) return;
3445
video.play(false, true);
3546
}
3647
}

src/modules/esl-media/core/esl-media-manager.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {ESLEventUtils} from '../../esl-event-listener/core/api';
22
import {isSafeContains} from '../../esl-utils/dom/traversing';
33
import {isVisible} from '../../esl-utils/dom/visible';
4-
import {ExportNs} from '../../esl-utils/environment/export-ns';
54
import {listen} from '../../esl-utils/decorators';
65

76
import type {ESLMedia} from './esl-media';
@@ -14,7 +13,6 @@ import type {ESLMedia} from './esl-media';
1413
*
1514
* @author Anastasia Lesun
1615
*/
17-
@ExportNs('MediaManager')
1816
export class ESLMediaManager {
1917
/** All managed instances */
2018
public instances: Set<ESLMedia> = new Set();

src/modules/esl-media/core/esl-media-provider.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export abstract class BaseProvider {
5858
public constructor(component: ESLMedia, config: MediaProviderConfig) {
5959
this.config = config;
6060
this.component = component;
61+
// Other-vice browser will handle play-in-viewport it differently
62+
if (this.component.playInViewport) this.config.autoplay = false;
63+
// If autoplay is enabled - dispatch before play event to ensure it is allowed
6164
if (this.config.autoplay) {
6265
this.config.autoplay = this.component._onBeforePlay('initial');
6366
}

src/modules/esl-media/core/esl-media.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ export class ESLMedia extends ESLBaseElement {
5555
return new ESLMediaManager();
5656
}
5757

58+
/** A minimum ratio to init media (in case of lazy=auto) */
59+
@prop(0.05) public RATIO_TO_ACTIVATE: number;
60+
/** A minimum ratio to stop media (in case of play in viewport option) */
61+
@prop(0.2) public RATIO_TO_STOP: number;
62+
/** A minimum ratio to play media (in case of play in viewport option) */
63+
@prop(0.33) public RATIO_TO_PLAY: number;
64+
5865
/** Event to dispatch on ready state */
5966
@prop('esl:media:ready') public READY_EVENT: string;
6067
/** Event to dispatch on error state */

src/modules/esl-utils/test/intersectionObserver.mock.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ export class IntersectionObserverMock implements IntersectionObserver {
5757
target: Element,
5858
init: Partial<IntersectionObserverEntry>
5959
): IntersectionObserverEntry {
60+
const intersectionRatio = init.intersectionRatio || 0;
6061
return {
6162
// Defaults
6263
target,
63-
intersectionRect: new RectMock(),
64-
rootBounds: new RectMock(),
65-
boundingClientRect: new RectMock(),
64+
intersectionRect: new RectMock(0, 0, 2000, 2000 * intersectionRatio),
65+
rootBounds: new RectMock(0, 0, 2000, 2000),
66+
boundingClientRect: new RectMock(0, 0, 2000, 2000),
6667
isIntersecting: false,
6768
intersectionRatio: 0,
6869
time: Date.now(),

0 commit comments

Comments
 (0)