Merge pull request #184 from dermotduffy/safari-fix
Fix lazy event viewer loading in Safari
This commit is contained in:
+49
-17
@@ -39,7 +39,9 @@ import './next-prev-control.js';
|
|||||||
|
|
||||||
import viewerStyle from '../scss/viewer.scss';
|
import viewerStyle from '../scss/viewer.scss';
|
||||||
|
|
||||||
const IMG_EMPTY = 'data:,';
|
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`;
|
||||||
|
const IMG_EMPTY = getEmptyImageSrc(16, 9);
|
||||||
|
|
||||||
@customElement('frigate-card-viewer')
|
@customElement('frigate-card-viewer')
|
||||||
export class FrigateCardViewer extends LitElement {
|
export class FrigateCardViewer extends LitElement {
|
||||||
@@ -171,9 +173,11 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
// (Folders are not media items that can be rendered).
|
// (Folders are not media items that can be rendered).
|
||||||
protected _slideToChild: Record<number, number> = {};
|
protected _slideToChild: Record<number, number> = {};
|
||||||
|
|
||||||
// A "map" from slide number to MediaShowInfo object or null if the slide has
|
// A "map" from slide number to MediaShowInfo object.
|
||||||
// been lazy loaded, but the MediaShowInfo object is not yet available.
|
protected _mediaShowInfo: Record<number, MediaShowInfo> = {};
|
||||||
protected _mediaShowInfo: Record<number, MediaShowInfo | null> = {};
|
|
||||||
|
// Whether or not a given slide has been successfully lazily loaded.
|
||||||
|
protected _slideHasBeenLazyLoaded: Record<number, boolean> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The updated lifecycle callback for this element.
|
* The updated lifecycle callback for this element.
|
||||||
@@ -414,16 +418,15 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slidesToLoad.forEach((index) => {
|
slidesToLoad.forEach((index) => {
|
||||||
// Only lazy loads slides that are not already loaded.
|
// Only lazy load slides that are not already loaded.
|
||||||
if (index in this._mediaShowInfo) {
|
if (this._slideHasBeenLazyLoaded[index]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._mediaShowInfo[index] = null;
|
this._slideHasBeenLazyLoaded[index] = true;
|
||||||
|
|
||||||
const slide = slides[index];
|
const slide = slides[index];
|
||||||
|
|
||||||
// Snapshots.
|
// Snapshots.
|
||||||
const img = slide.querySelector('img');
|
const img = slide.querySelector('img') as HTMLImageElement;
|
||||||
|
|
||||||
// Frigate >= 0.9.0+ clips.
|
// Frigate >= 0.9.0+ clips.
|
||||||
const hls_player = slide.querySelector(
|
const hls_player = slide.querySelector(
|
||||||
@@ -512,10 +515,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
|
|
||||||
this._carousel.slidesInView(true).forEach((slideIndex) => {
|
this._carousel.slidesInView(true).forEach((slideIndex) => {
|
||||||
if (slideIndex in this._mediaShowInfo) {
|
if (slideIndex in this._mediaShowInfo) {
|
||||||
const mediaShowInfo = this._mediaShowInfo[slideIndex];
|
dispatchExistingMediaShowInfoAsEvent(this, this._mediaShowInfo[slideIndex]);
|
||||||
if (mediaShowInfo) {
|
|
||||||
dispatchExistingMediaShowInfoAsEvent(this, mediaShowInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -546,13 +546,39 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
slideIndex: number,
|
slideIndex: number,
|
||||||
mediaShowInfo?: MediaShowInfo | null,
|
mediaShowInfo?: MediaShowInfo | null,
|
||||||
): void {
|
): void {
|
||||||
// isValidMediaShowInfo is used to weed out the initial load of the
|
// isValidMediaShowInfo is used to prevent saving media info that will be
|
||||||
// transparent 1x1 placeholders.
|
// rejected upstream.
|
||||||
if (mediaShowInfo && isValidMediaShowInfo(mediaShowInfo)) {
|
if (this.viewerConfig && mediaShowInfo && isValidMediaShowInfo(mediaShowInfo)) {
|
||||||
|
const firstMediaLoad = !Object.keys(this._mediaShowInfo).length;
|
||||||
this._mediaShowInfo[slideIndex] = mediaShowInfo;
|
this._mediaShowInfo[slideIndex] = mediaShowInfo;
|
||||||
if (this._carousel && this._carousel?.slidesInView(true).includes(slideIndex)) {
|
if (this._carousel && this._carousel?.slidesInView(true).includes(slideIndex)) {
|
||||||
dispatchExistingMediaShowInfoAsEvent(this, mediaShowInfo);
|
dispatchExistingMediaShowInfoAsEvent(this, mediaShowInfo);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Images need a width/height from initial load, and browsers will assume
|
||||||
|
* that the aspect ratio of the initial dummy-image load will persist. In
|
||||||
|
* lazy-loading, this can cause a 1x1 pixel dummy image to cause the
|
||||||
|
* browser to assume all images will be square, so the whole carousel will
|
||||||
|
* have the wrong aspect-ratio until every single image has been lazily
|
||||||
|
* loaded. To avoid this, we use a 16:9 dummy image at first (most
|
||||||
|
* likely?) and once the first piece of real media has been loaded, all
|
||||||
|
* dummy images are replaced with dummy images that match the aspect ratio
|
||||||
|
* of the real image. It still might be wrong, but it's the best option
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
if (firstMediaLoad && this.viewerConfig.lazy_load) {
|
||||||
|
const replacementImageSrc = getEmptyImageSrc(
|
||||||
|
mediaShowInfo.width,
|
||||||
|
mediaShowInfo.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.renderRoot.querySelectorAll('.embla__container img').forEach((img) => {
|
||||||
|
const imageElement: HTMLImageElement = img as HTMLImageElement;
|
||||||
|
if (imageElement.src === IMG_EMPTY) {
|
||||||
|
imageElement.src = replacementImageSrc
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +593,11 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
): TemplateResult | void {
|
): TemplateResult | void {
|
||||||
// media that can be expanded (folders) cannot be resolved to a single media
|
// media that can be expanded (folders) cannot be resolved to a single media
|
||||||
// item, skip them.
|
// item, skip them.
|
||||||
if (!this.view || !this.viewerConfig || !BrowseMediaUtil.isTrueMedia(mediaToRender)) {
|
if (
|
||||||
|
!this.view ||
|
||||||
|
!this.viewerConfig ||
|
||||||
|
!BrowseMediaUtil.isTrueMedia(mediaToRender)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +668,9 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@load="${(e: Event) => {
|
@load="${(e: Event) => {
|
||||||
|
if (this.viewerConfig && (!this.viewerConfig.lazy_load || this._slideHasBeenLazyLoaded[slideIndex])) {
|
||||||
this._mediaShowInfoHandler(slideIndex, createMediaShowInfo(e));
|
this._mediaShowInfoHandler(slideIndex, createMediaShowInfo(e));
|
||||||
|
}
|
||||||
}}"
|
}}"
|
||||||
/>`}
|
/>`}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ ha-hls-player {
|
|||||||
|
|
||||||
img,video {
|
img,video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: auto;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user