Make autoplay/pause work correctly with lazyloading.
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
"@material/rtl": "^13.0.0",
|
"@material/rtl": "^13.0.0",
|
||||||
"custom-card-helpers": "^1.8.0",
|
"custom-card-helpers": "^1.8.0",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"embla-carousel": "^6.1.0",
|
"embla-carousel": "^6.1.1",
|
||||||
"home-assistant-js-websocket": "^5.11.1",
|
"home-assistant-js-websocket": "^5.11.1",
|
||||||
"lit": "^2.0.2",
|
"lit": "^2.0.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
|
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
|
||||||
import EmblaCarousel, { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
import EmblaCarousel, {
|
||||||
|
EmblaCarouselType,
|
||||||
|
EmblaOptionsType,
|
||||||
|
EmblaPluginType,
|
||||||
|
} from 'embla-carousel';
|
||||||
|
|
||||||
import { dispatchFrigateCardEvent } from '../common';
|
import { dispatchFrigateCardEvent } from '../common';
|
||||||
|
|
||||||
@@ -11,6 +15,7 @@ export interface CarouselSelect {
|
|||||||
|
|
||||||
export class FrigateCardCarousel extends LitElement {
|
export class FrigateCardCarousel extends LitElement {
|
||||||
protected _carousel?: EmblaCarouselType;
|
protected _carousel?: EmblaCarouselType;
|
||||||
|
protected _plugins: Record<string, EmblaPluginType> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to a particular slide.
|
* Scroll to a particular slide.
|
||||||
@@ -65,6 +70,7 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
if (this._carousel) {
|
if (this._carousel) {
|
||||||
this._carousel.destroy();
|
this._carousel.destroy();
|
||||||
}
|
}
|
||||||
|
this._plugins = {};
|
||||||
this._carousel = undefined;
|
this._carousel = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +83,13 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
if (carouselNode) {
|
if (carouselNode) {
|
||||||
this._carousel = EmblaCarousel(carouselNode, this._getOptions(), this._getPlugins());
|
const plugins = this._getPlugins() ?? [];
|
||||||
|
this._plugins = plugins.reduce((acc, cur) => {
|
||||||
|
acc[cur.name] = cur;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this._carousel = EmblaCarousel(carouselNode, this._getOptions(), plugins);
|
||||||
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
||||||
this._carousel.on('select', () => {
|
this._carousel.on('select', () => {
|
||||||
const selected = this.carouselSelected();
|
const selected = this.carouselSelected();
|
||||||
|
|||||||
@@ -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 { EmblaCarouselType, EmblaPluginType } from 'embla-carousel';
|
||||||
import { FrigateCardMediaPlayer } from '../../types';
|
import { FrigateCardMediaPlayer } from '../../types';
|
||||||
|
|
||||||
export type MediaAutoplayOptionsType = {
|
export type MediaAutoPlayPauseOptionsType = {
|
||||||
autoplay?: boolean;
|
autoplay?: boolean;
|
||||||
autopause?: boolean;
|
autopause?: boolean;
|
||||||
playerSelector: string;
|
playerSelector: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultOptions: Partial<MediaAutoplayOptionsType> = {
|
export const defaultOptions: Partial<MediaAutoPlayPauseOptionsType> = {
|
||||||
autoplay: true,
|
// Frigate card media autoplays when the media loads, not necessarily when the
|
||||||
|
// slide is selected.
|
||||||
|
autoplay: false,
|
||||||
autopause: true,
|
autopause: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MediaAutoplayType = EmblaPluginType<MediaAutoplayOptionsType>;
|
export type MediaAutoPlayPauseType = EmblaPluginType<MediaAutoPlayPauseOptionsType> & {
|
||||||
|
play: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export function MediaAutoplay(
|
export function MediaAutoPlayPause(
|
||||||
userOptions?: MediaAutoplayOptionsType,
|
userOptions?: MediaAutoPlayPauseOptionsType,
|
||||||
): MediaAutoplayType {
|
): MediaAutoPlayPauseType {
|
||||||
const options = Object.assign({}, defaultOptions, userOptions);
|
const options = Object.assign({}, defaultOptions, userOptions);
|
||||||
|
|
||||||
let carousel: EmblaCarouselType;
|
let carousel: EmblaCarouselType;
|
||||||
@@ -31,12 +35,12 @@ export function MediaAutoplay(
|
|||||||
|
|
||||||
if (options.autopause) {
|
if (options.autopause) {
|
||||||
carousel.on('destroy', pauseAllHandler);
|
carousel.on('destroy', pauseAllHandler);
|
||||||
carousel.on('select', autopausePreviousHandler);
|
carousel.on('select', pausePrevious);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.autoplay) {
|
if (options.autoplay) {
|
||||||
carousel.on('select', autoplayCurrentHandler);
|
carousel.on('select', play);
|
||||||
carousel.on('init', autoplayCurrentHandler);
|
carousel.on('init', play);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,12 +50,12 @@ export function MediaAutoplay(
|
|||||||
function destroy(): void {
|
function destroy(): void {
|
||||||
if (options.autopause) {
|
if (options.autopause) {
|
||||||
carousel.off('destroy', pauseAllHandler);
|
carousel.off('destroy', pauseAllHandler);
|
||||||
carousel.off('select', autopausePreviousHandler);
|
carousel.off('select', pausePrevious);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.autoplay) {
|
if (options.autoplay) {
|
||||||
carousel.off('select', autoplayCurrentHandler);
|
carousel.off('select', play);
|
||||||
carousel.off('init', autoplayCurrentHandler);
|
carousel.off('init', play);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +64,8 @@ export function MediaAutoplay(
|
|||||||
* @param slide
|
* @param slide
|
||||||
* @returns A FrigateCardMediaPlayer object or `null`.
|
* @returns A FrigateCardMediaPlayer object or `null`.
|
||||||
*/
|
*/
|
||||||
function getPlayer(slide: HTMLElement): FrigateCardMediaPlayer | null {
|
function getPlayer(slide: HTMLElement | undefined): FrigateCardMediaPlayer | null {
|
||||||
return slide.querySelector(options.playerSelector) as FrigateCardMediaPlayer | null;
|
return slide?.querySelector(options.playerSelector) as FrigateCardMediaPlayer | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,22 +78,23 @@ export function MediaAutoplay(
|
|||||||
/**
|
/**
|
||||||
* Autoplay the current slide.
|
* Autoplay the current slide.
|
||||||
*/
|
*/
|
||||||
function autoplayCurrentHandler(): void {
|
function play(): void {
|
||||||
getPlayer(slides[carousel.selectedScrollSnap()])?.play();
|
getPlayer(slides[carousel.selectedScrollSnap()])?.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autopause the previous slide.
|
* Autopause the previous slide.
|
||||||
*/
|
*/
|
||||||
function autopausePreviousHandler(): void {
|
function pausePrevious(): void {
|
||||||
getPlayer(slides[carousel.previousScrollSnap()])?.pause();
|
getPlayer(slides[carousel.previousScrollSnap()])?.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
const self: MediaAutoplayType = {
|
const self: MediaAutoPlayPauseType = {
|
||||||
name: 'MediaAutoplay',
|
name: 'MediaAutoPlayPause',
|
||||||
options,
|
options,
|
||||||
init,
|
init,
|
||||||
destroy,
|
destroy,
|
||||||
|
play,
|
||||||
};
|
};
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-4
@@ -29,7 +29,8 @@ import { BrowseMediaUtil } from '../browse-media-util.js';
|
|||||||
import { ConditionState, getOverriddenConfig } from '../card-condition.js';
|
import { ConditionState, getOverriddenConfig } from '../card-condition.js';
|
||||||
import { FrigateCardMediaCarousel } from './media-carousel.js';
|
import { FrigateCardMediaCarousel } from './media-carousel.js';
|
||||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||||
import { MediaAutoplay } from './embla-plugins/media-autoplay.js';
|
import { Lazyload } from './embla-plugins/lazyload.js';
|
||||||
|
import { MediaAutoPlayPause } from './embla-plugins/media-autoplay.js';
|
||||||
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
import { localize } from '../localize/localize.js';
|
import { localize } from '../localize/localize.js';
|
||||||
@@ -298,7 +299,14 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
*/
|
*/
|
||||||
protected _getPlugins(): EmblaPluginType[] | undefined {
|
protected _getPlugins(): EmblaPluginType[] | undefined {
|
||||||
return [
|
return [
|
||||||
MediaAutoplay({
|
...(this.liveConfig?.lazy_load
|
||||||
|
? [
|
||||||
|
Lazyload({
|
||||||
|
lazyloadCallback: this._lazyLoadSlide.bind(this),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
MediaAutoPlayPause({
|
||||||
playerSelector: 'frigate-card-live-provider',
|
playerSelector: 'frigate-card-live-provider',
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -394,7 +402,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
|
|
||||||
return html` <div class="embla__slide">
|
return html` <div class="embla__slide">
|
||||||
<frigate-card-live-provider
|
<frigate-card-live-provider
|
||||||
?disabled=${this._isLazyLoading()}
|
?disabled=${this.liveConfig.lazy_load}
|
||||||
.cameraConfig=${cameraConfig}
|
.cameraConfig=${cameraConfig}
|
||||||
.label=${getCameraTitle(this.hass, cameraConfig)}
|
.label=${getCameraTitle(this.hass, cameraConfig)}
|
||||||
.liveConfig=${config}
|
.liveConfig=${config}
|
||||||
@@ -674,7 +682,11 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
* Play the video.
|
* Play the video.
|
||||||
*/
|
*/
|
||||||
public play(): void {
|
public play(): void {
|
||||||
this._getPlayer()?.play();
|
this._getPlayer()?.play().catch((_) => {
|
||||||
|
// WebRTC appears to generate additional spurious load events, which may
|
||||||
|
// result in loads after a play() call, which causes the browser to spam
|
||||||
|
// the logs unless the promise rejection is handled here.
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import './next-prev-control.js';
|
|||||||
import mediaCarouselStyle from '../scss/media-carousel.scss';
|
import mediaCarouselStyle from '../scss/media-carousel.scss';
|
||||||
|
|
||||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||||
|
import { MediaAutoPlayPauseType } from './embla-plugins/media-autoplay.js';
|
||||||
|
|
||||||
const getEmptyImageSrc = (width: number, height: number) =>
|
const getEmptyImageSrc = (width: number, height: number) =>
|
||||||
`data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"%3E%3C/svg%3E`;
|
`data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"%3E%3C/svg%3E`;
|
||||||
@@ -24,31 +25,30 @@ export const IMG_EMPTY = getEmptyImageSrc(16, 9);
|
|||||||
export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
||||||
// A "map" from slide number to MediaShowInfo object.
|
// A "map" from slide number to MediaShowInfo object.
|
||||||
protected _mediaShowInfo: Record<number, MediaShowInfo> = {};
|
protected _mediaShowInfo: Record<number, MediaShowInfo> = {};
|
||||||
|
|
||||||
// Whether or not a given slide has been successfully lazily loaded.
|
|
||||||
protected _slideHasBeenLazyLoaded: Record<number, boolean> = {};
|
|
||||||
|
|
||||||
protected _nextControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
protected _nextControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
||||||
protected _previousControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
protected _previousControlRef: Ref<FrigateCardNextPreviousControl> = createRef();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
* Play the media on the selected slide.
|
||||||
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
|
||||||
* should lazy load, etc. `null` means lazy loading is disabled and everything
|
|
||||||
* should load simultaneously.
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
protected _getLazyLoadCount(): number | null {
|
protected _playSelectedMediaHandler(): void {
|
||||||
// Defaults to fully-lazy loading.
|
(this._plugins['MediaAutoPlayPause'] as MediaAutoPlayPauseType | undefined)?.play();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if lazy loading is being used.
|
* Component connected callback.
|
||||||
* @returns `true` is lazy loading is in use.
|
|
||||||
*/
|
*/
|
||||||
protected _isLazyLoading(): boolean {
|
connectedCallback(): void {
|
||||||
return this._getLazyLoadCount() !== null;
|
super.connectedCallback();
|
||||||
|
this.addEventListener('frigate-card:media-show', this._playSelectedMediaHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component disconnected callback.
|
||||||
|
*/
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.removeEventListener('frigate-card:media-show', this._playSelectedMediaHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _destroyCarousel(): void {
|
protected _destroyCarousel(): void {
|
||||||
@@ -61,9 +61,6 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
|||||||
// media would not reload) -- as such, leave this alone on carousel
|
// media would not reload) -- as such, leave this alone on carousel
|
||||||
// destroy. New media in that slide will replace the prior contents on
|
// destroy. New media in that slide will replace the prior contents on
|
||||||
// load.
|
// load.
|
||||||
// * this._slideHasBeenLazyLoaded: This is a performance optimization and
|
|
||||||
// can be safely reset.
|
|
||||||
this._slideHasBeenLazyLoaded = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,13 +89,6 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
|||||||
carousel?.on('init', this._adaptiveHeightSetHandler.bind(this));
|
carousel?.on('init', this._adaptiveHeightSetHandler.bind(this));
|
||||||
carousel?.on('select', this._adaptiveHeightSetHandler.bind(this));
|
carousel?.on('select', this._adaptiveHeightSetHandler.bind(this));
|
||||||
carousel?.on('resize', this._adaptiveHeightSetHandler.bind(this));
|
carousel?.on('resize', this._adaptiveHeightSetHandler.bind(this));
|
||||||
|
|
||||||
if (this._getLazyLoadCount() != null) {
|
|
||||||
// Load media as the carousel is moved (if lazy loading is in use).
|
|
||||||
carousel?.on('init', this._lazyLoadMediaHandler.bind(this));
|
|
||||||
carousel?.on('select', this._lazyLoadMediaHandler.bind(this));
|
|
||||||
carousel?.on('resize', this._lazyLoadMediaHandler.bind(this));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,54 +155,6 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Lazily load media in the carousel.
|
|
||||||
*/
|
|
||||||
protected _lazyLoadMediaHandler(): void {
|
|
||||||
if (!this._carousel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lazyLoadCount = this._getLazyLoadCount();
|
|
||||||
if (lazyLoadCount === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slides = this._carousel.slideNodes();
|
|
||||||
const slidesInView = this._carousel.slidesInView(true);
|
|
||||||
const slidesToLoad = new Set<number>();
|
|
||||||
|
|
||||||
const minSlide = Math.min(...slidesInView);
|
|
||||||
const maxSlide = Math.max(...slidesInView);
|
|
||||||
|
|
||||||
// Lazily load 'lazyLoadCount' 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 (this._slideHasBeenLazyLoaded[index]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._slideHasBeenLazyLoaded[index] = true;
|
|
||||||
this._lazyLoadSlide(index, slides[index]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lazy load a slide.
|
|
||||||
* @param _index The index of the slide to lazy load.
|
|
||||||
* @param _slide The slide to lazy load.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
protected _lazyLoadSlide(_index: number, _slide: HTMLElement): void {
|
|
||||||
// To be overridden in children.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire a media show event when a slide is selected.
|
* Fire a media show event when a slide is selected.
|
||||||
*/
|
*/
|
||||||
@@ -282,7 +224,7 @@ export class FrigateCardMediaCarousel extends FrigateCardCarousel {
|
|||||||
* available.
|
* available.
|
||||||
*/
|
*/
|
||||||
const firstMediaLoad = !Object.keys(this._mediaShowInfo).length;
|
const firstMediaLoad = !Object.keys(this._mediaShowInfo).length;
|
||||||
if (firstMediaLoad && this._getLazyLoadCount() != null) {
|
if (firstMediaLoad) {
|
||||||
const replacementImageSrc = getEmptyImageSrc(
|
const replacementImageSrc = getEmptyImageSrc(
|
||||||
mediaShowInfo.width,
|
mediaShowInfo.width,
|
||||||
mediaShowInfo.height,
|
mediaShowInfo.height,
|
||||||
|
|||||||
+19
-21
@@ -29,7 +29,8 @@ import {
|
|||||||
FrigateCardThumbnailCarousel,
|
FrigateCardThumbnailCarousel,
|
||||||
ThumbnailCarouselTap,
|
ThumbnailCarouselTap,
|
||||||
} from './thumbnail-carousel.js';
|
} from './thumbnail-carousel.js';
|
||||||
import { MediaAutoplay } from './embla-plugins/media-autoplay.js';
|
import { MediaAutoPlayPause } from './embla-plugins/media-autoplay.js';
|
||||||
|
import { Lazyload, LazyloadType } from './embla-plugins/lazyload.js';
|
||||||
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
import {
|
import {
|
||||||
@@ -216,7 +217,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
async ([target]: (BrowseMediaSource | undefined)[]): Promise<void> => {
|
async ([target]: (BrowseMediaSource | undefined)[]): Promise<void> => {
|
||||||
for (
|
for (
|
||||||
let i = 0;
|
let i = 0;
|
||||||
!this._isLazyLoading() &&
|
!this.viewerConfig?.lazy_load &&
|
||||||
this.hass &&
|
this.hass &&
|
||||||
target &&
|
target &&
|
||||||
target.children &&
|
target.children &&
|
||||||
@@ -298,22 +299,19 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
* Get the Embla plugins to use.
|
* Get the Embla plugins to use.
|
||||||
* @returns An EmblaOptionsType object or undefined for no options.
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
*/
|
*/
|
||||||
protected _getPlugins(): EmblaPluginType[] | undefined {
|
protected _getPlugins(): EmblaPluginType[] | undefined {
|
||||||
return [MediaAutoplay({
|
return [
|
||||||
autoplay: this.viewerConfig?.autoplay_clip,
|
...(this.viewerConfig?.lazy_load
|
||||||
playerSelector: 'frigate-card-ha-hls-player' })];
|
? [
|
||||||
}
|
Lazyload({
|
||||||
|
lazyloadCallback: this._lazyLoadSlide.bind(this),
|
||||||
/**
|
}),
|
||||||
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
]
|
||||||
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
: []),
|
||||||
* should lazy load, etc. `null` means lazy loading is disabled and everything
|
MediaAutoPlayPause({
|
||||||
* should load simultaneously.
|
playerSelector: 'frigate-card-ha-hls-player',
|
||||||
* @returns
|
}),
|
||||||
*/
|
];
|
||||||
protected _getLazyLoadCount(): number | null {
|
|
||||||
// Defaults to fully-lazy loading.
|
|
||||||
return this.viewerConfig?.lazy_load === false ? null : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -591,7 +589,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
|
|
||||||
// If lazy loading is not enabled, wait for the media resolver task to
|
// If lazy loading is not enabled, wait for the media resolver task to
|
||||||
// complete and show a progress indictator until this.
|
// complete and show a progress indictator until this.
|
||||||
if (!this._isLazyLoading() && !this._isMediaFullyResolved()) {
|
if (!this.viewerConfig?.lazy_load && !this._isMediaFullyResolved()) {
|
||||||
return html`${this._mediaResolutionTask.render({
|
return html`${this._mediaResolutionTask.render({
|
||||||
initial: () => renderProgressIndicator(),
|
initial: () => renderProgressIndicator(),
|
||||||
pending: () => renderProgressIndicator(),
|
pending: () => renderProgressIndicator(),
|
||||||
@@ -658,7 +656,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lazyLoad = this._isLazyLoading();
|
const lazyLoad = this.viewerConfig.lazy_load;
|
||||||
const resolvedMedia = this.resolvedMediaCache?.get(mediaToRender.media_content_id);
|
const resolvedMedia = this.resolvedMediaCache?.get(mediaToRender.media_content_id);
|
||||||
if (!resolvedMedia && !lazyLoad) {
|
if (!resolvedMedia && !lazyLoad) {
|
||||||
return;
|
return;
|
||||||
@@ -701,7 +699,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
// images in media-carousel.ts). Here we need to only call the
|
// images in media-carousel.ts). Here we need to only call the
|
||||||
// media load handler on a 'real' load.
|
// media load handler on a 'real' load.
|
||||||
!lazyLoad ||
|
!lazyLoad ||
|
||||||
this._slideHasBeenLazyLoaded[slideIndex]
|
(this._plugins['Lazyload'] as LazyloadType | undefined)?.hasLazyloaded(slideIndex)
|
||||||
) {
|
) {
|
||||||
this._mediaLoadedHandler(slideIndex, createMediaShowInfo(e));
|
this._mediaLoadedHandler(slideIndex, createMediaShowInfo(e));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user