Make autoplay/pause work correctly with lazyloading.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { EmblaCarouselType, EmblaPluginType } from 'embla-carousel';
|
||||
|
||||
export type LazyloadOptionsType = {
|
||||
count?: number;
|
||||
lazyloadCallback: (index: number, slide: HTMLElement) => void;
|
||||
};
|
||||
|
||||
export const defaultOptions: Partial<LazyloadOptionsType> = {
|
||||
count: 0,
|
||||
};
|
||||
|
||||
export type LazyloadType = EmblaPluginType<LazyloadOptionsType> & {
|
||||
hasLazyloaded: (index: number) => boolean;
|
||||
};
|
||||
|
||||
export function Lazyload(userOptions?: LazyloadOptionsType): LazyloadType {
|
||||
const options = Object.assign({}, defaultOptions, userOptions);
|
||||
|
||||
let carousel: EmblaCarouselType;
|
||||
let slides: HTMLElement[];
|
||||
const isSlideLazyloaded: Record<number, boolean> = {};
|
||||
|
||||
/**
|
||||
* Initialize the plugin.
|
||||
*/
|
||||
function init(embla: EmblaCarouselType): void {
|
||||
carousel = embla;
|
||||
slides = carousel.slideNodes();
|
||||
|
||||
carousel.on('init', lazyLoadHandler);
|
||||
carousel.on('select', lazyLoadHandler);
|
||||
carousel.on('resize', lazyLoadHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the plugin.
|
||||
*/
|
||||
function destroy(): void {
|
||||
carousel.off('init', lazyLoadHandler);
|
||||
carousel.off('select', lazyLoadHandler);
|
||||
carousel.off('resize', lazyLoadHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a slide index has been lazily loaded.
|
||||
* @param index Slide index.
|
||||
* @returns `true` if the slide has been lazily loaded.
|
||||
*/
|
||||
function hasLazyloaded(index: number): boolean {
|
||||
return !!isSlideLazyloaded[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily load media in the carousel.
|
||||
*/
|
||||
function lazyLoadHandler(): void {
|
||||
const lazyLoadCount = options.count ?? 0;
|
||||
const slidesInView = carousel.slidesInView(true);
|
||||
const slidesToLoad = new Set<number>();
|
||||
|
||||
const minSlide = Math.min(...slidesInView);
|
||||
const maxSlide = Math.max(...slidesInView);
|
||||
|
||||
// Lazily load 'count' slides on either side of the slides in view.
|
||||
for (let i = 1; i <= lazyLoadCount && minSlide - i >= 0; i++) {
|
||||
slidesToLoad.add(minSlide - i);
|
||||
}
|
||||
slidesInView.forEach((index) => slidesToLoad.add(index));
|
||||
for (let i = 1; i <= lazyLoadCount && maxSlide + i < slides.length; i++) {
|
||||
slidesToLoad.add(maxSlide + i);
|
||||
}
|
||||
|
||||
slidesToLoad.forEach((index) => {
|
||||
// Only lazy load slides that are not already loaded.
|
||||
if (isSlideLazyloaded[index]) {
|
||||
return;
|
||||
}
|
||||
isSlideLazyloaded[index] = true;
|
||||
options.lazyloadCallback(index, slides[index]);
|
||||
});
|
||||
}
|
||||
|
||||
const self: LazyloadType = {
|
||||
name: 'Lazyload',
|
||||
options,
|
||||
init,
|
||||
destroy,
|
||||
hasLazyloaded,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
import { EmblaCarouselType, EmblaPluginType } from 'embla-carousel';
|
||||
import { FrigateCardMediaPlayer } from '../../types';
|
||||
|
||||
export type MediaAutoplayOptionsType = {
|
||||
export type MediaAutoPlayPauseOptionsType = {
|
||||
autoplay?: boolean;
|
||||
autopause?: boolean;
|
||||
playerSelector: string;
|
||||
};
|
||||
|
||||
export const defaultOptions: Partial<MediaAutoplayOptionsType> = {
|
||||
autoplay: true,
|
||||
export const defaultOptions: Partial<MediaAutoPlayPauseOptionsType> = {
|
||||
// Frigate card media autoplays when the media loads, not necessarily when the
|
||||
// slide is selected.
|
||||
autoplay: false,
|
||||
autopause: true,
|
||||
};
|
||||
|
||||
export type MediaAutoplayType = EmblaPluginType<MediaAutoplayOptionsType>;
|
||||
export type MediaAutoPlayPauseType = EmblaPluginType<MediaAutoPlayPauseOptionsType> & {
|
||||
play: () => void;
|
||||
}
|
||||
|
||||
export function MediaAutoplay(
|
||||
userOptions?: MediaAutoplayOptionsType,
|
||||
): MediaAutoplayType {
|
||||
export function MediaAutoPlayPause(
|
||||
userOptions?: MediaAutoPlayPauseOptionsType,
|
||||
): MediaAutoPlayPauseType {
|
||||
const options = Object.assign({}, defaultOptions, userOptions);
|
||||
|
||||
let carousel: EmblaCarouselType;
|
||||
@@ -31,12 +35,12 @@ export function MediaAutoplay(
|
||||
|
||||
if (options.autopause) {
|
||||
carousel.on('destroy', pauseAllHandler);
|
||||
carousel.on('select', autopausePreviousHandler);
|
||||
carousel.on('select', pausePrevious);
|
||||
}
|
||||
|
||||
if (options.autoplay) {
|
||||
carousel.on('select', autoplayCurrentHandler);
|
||||
carousel.on('init', autoplayCurrentHandler);
|
||||
carousel.on('select', play);
|
||||
carousel.on('init', play);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +50,12 @@ export function MediaAutoplay(
|
||||
function destroy(): void {
|
||||
if (options.autopause) {
|
||||
carousel.off('destroy', pauseAllHandler);
|
||||
carousel.off('select', autopausePreviousHandler);
|
||||
carousel.off('select', pausePrevious);
|
||||
}
|
||||
|
||||
if (options.autoplay) {
|
||||
carousel.off('select', autoplayCurrentHandler);
|
||||
carousel.off('init', autoplayCurrentHandler);
|
||||
carousel.off('select', play);
|
||||
carousel.off('init', play);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +64,8 @@ export function MediaAutoplay(
|
||||
* @param slide
|
||||
* @returns A FrigateCardMediaPlayer object or `null`.
|
||||
*/
|
||||
function getPlayer(slide: HTMLElement): FrigateCardMediaPlayer | null {
|
||||
return slide.querySelector(options.playerSelector) as FrigateCardMediaPlayer | null;
|
||||
function getPlayer(slide: HTMLElement | undefined): FrigateCardMediaPlayer | null {
|
||||
return slide?.querySelector(options.playerSelector) as FrigateCardMediaPlayer | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,22 +78,23 @@ export function MediaAutoplay(
|
||||
/**
|
||||
* Autoplay the current slide.
|
||||
*/
|
||||
function autoplayCurrentHandler(): void {
|
||||
function play(): void {
|
||||
getPlayer(slides[carousel.selectedScrollSnap()])?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Autopause the previous slide.
|
||||
*/
|
||||
function autopausePreviousHandler(): void {
|
||||
function pausePrevious(): void {
|
||||
getPlayer(slides[carousel.previousScrollSnap()])?.pause();
|
||||
}
|
||||
|
||||
const self: MediaAutoplayType = {
|
||||
name: 'MediaAutoplay',
|
||||
const self: MediaAutoPlayPauseType = {
|
||||
name: 'MediaAutoPlayPause',
|
||||
options,
|
||||
init,
|
||||
destroy,
|
||||
play,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user