mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-11-24 08:42:24 +02:00
Merge pull request #509 from bpatrik/feature/performance
Implementing next photo preloading
This commit is contained in:
commit
b837ed167c
@ -154,6 +154,21 @@
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
25% {
|
||||
opacity: 1.0;
|
||||
}
|
||||
75% {
|
||||
opacity: 1.0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes blink {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
|
@ -10,20 +10,20 @@ import {
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { MediaDTOUtils } from '../../../../../../common/entities/MediaDTO';
|
||||
import { FullScreenService } from '../../fullscreen.service';
|
||||
import { GalleryPhotoComponent } from '../../grid/photo/photo.grid.gallery.component';
|
||||
import { Observable, Subscription, timer } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { PhotoDTO } from '../../../../../../common/entities/PhotoDTO';
|
||||
import { GalleryLightboxMediaComponent } from '../media/media.lightbox.gallery.component';
|
||||
import { Config } from '../../../../../../common/config/public/Config';
|
||||
import {MediaDTOUtils} from '../../../../../../common/entities/MediaDTO';
|
||||
import {FullScreenService} from '../../fullscreen.service';
|
||||
import {GalleryPhotoComponent} from '../../grid/photo/photo.grid.gallery.component';
|
||||
import {Observable, Subscription, timer} from 'rxjs';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {PhotoDTO} from '../../../../../../common/entities/PhotoDTO';
|
||||
import {GalleryLightboxMediaComponent} from '../media/media.lightbox.gallery.component';
|
||||
import {Config} from '../../../../../../common/config/public/Config';
|
||||
import {
|
||||
SearchQueryTypes,
|
||||
TextSearch,
|
||||
TextSearchQueryMatchTypes,
|
||||
} from '../../../../../../common/entities/SearchQueryDTO';
|
||||
import { AuthenticationService } from '../../../../model/network/authentication.service';
|
||||
import {AuthenticationService} from '../../../../model/network/authentication.service';
|
||||
|
||||
export enum PlayBackStates {
|
||||
Paused = 1,
|
||||
@ -39,7 +39,7 @@ export enum PlayBackStates {
|
||||
export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
|
||||
readonly MAX_ZOOM = 10;
|
||||
|
||||
@ViewChild('root', { static: false }) root: ElementRef;
|
||||
@ViewChild('root', {static: false}) root: ElementRef;
|
||||
|
||||
@Output() closed = new EventEmitter();
|
||||
@Output() toggleInfoPanel = new EventEmitter();
|
||||
@ -47,10 +47,10 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
|
||||
@Output() nextPhoto = new EventEmitter();
|
||||
@Output() previousPhoto = new EventEmitter();
|
||||
|
||||
@Input() navigation = { hasPrev: true, hasNext: true };
|
||||
@Input() navigation = {hasPrev: true, hasNext: true};
|
||||
@Input() activePhoto: GalleryPhotoComponent;
|
||||
@Input() mediaElement: GalleryLightboxMediaComponent;
|
||||
@Input() photoFrameDim = { width: 1, height: 1, aspect: 1 };
|
||||
@Input() photoFrameDim = {width: 1, height: 1, aspect: 1};
|
||||
|
||||
public readonly facesEnabled = Config.Client.Faces.enabled;
|
||||
|
||||
@ -60,14 +60,14 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
|
||||
public controllersDimmed = false;
|
||||
public controllersAlwaysOn = false;
|
||||
public controllersVisible = true;
|
||||
public drag = { x: 0, y: 0 };
|
||||
public drag = {x: 0, y: 0};
|
||||
public SearchQueryTypes = SearchQueryTypes;
|
||||
public faceContainerDim = { width: 0, height: 0 };
|
||||
public faceContainerDim = {width: 0, height: 0};
|
||||
public searchEnabled: boolean;
|
||||
private visibilityTimer: number = null;
|
||||
private timer: Observable<number>;
|
||||
private timerSub: Subscription;
|
||||
private prevDrag = { x: 0, y: 0 };
|
||||
private prevDrag = {x: 0, y: 0};
|
||||
private prevZoom = 1;
|
||||
|
||||
constructor(
|
||||
@ -257,42 +257,33 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
private showNextMedia = () => {
|
||||
if (this.mediaElement.imageLoadFinished.this === false ||
|
||||
this.mediaElement.imageLoadFinished.next === false) {
|
||||
return;
|
||||
}
|
||||
// do not skip video if its playing
|
||||
if (
|
||||
this.activePhoto &&
|
||||
this.activePhoto.gridMedia.isVideo() &&
|
||||
!this.mediaElement.Paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.nextPhoto.emit();
|
||||
};
|
||||
|
||||
public play(): void {
|
||||
this.pause();
|
||||
this.timerSub = this.timer
|
||||
.pipe(filter((t) => t % 2 === 0))
|
||||
.subscribe(() => {
|
||||
if (this.mediaElement.imageLoadFinished === false) {
|
||||
return;
|
||||
}
|
||||
// do not skip video if its playing
|
||||
if (
|
||||
this.activePhoto &&
|
||||
this.activePhoto.gridMedia.isVideo() &&
|
||||
!this.mediaElement.Paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.nextPhoto.emit();
|
||||
});
|
||||
.subscribe(this.showNextMedia);
|
||||
this.playBackState = PlayBackStates.Play;
|
||||
}
|
||||
|
||||
public fastForward(): void {
|
||||
this.pause();
|
||||
this.timerSub = this.timer.subscribe(() => {
|
||||
if (this.mediaElement.imageLoadFinished === false) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.activePhoto &&
|
||||
this.activePhoto.gridMedia.isVideo() &&
|
||||
!this.mediaElement.Paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.nextPhoto.emit();
|
||||
});
|
||||
this.timerSub = this.timer.subscribe(this.showNextMedia);
|
||||
this.playBackState = PlayBackStates.FastForward;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
<div class="lightbox" #lightbox>
|
||||
<app-gallery-lightbox-media [gridMedia]="activePhoto ? activePhoto.gridMedia : null"
|
||||
[nextGridMedia]="NexGridMedia"
|
||||
[loadMedia]="!animating"
|
||||
[zoom]="controls ? controls.Zoom : 1"
|
||||
[drag]="controls ? controls.drag : {x:0,y:0}"
|
||||
|
@ -15,6 +15,7 @@ import {ContentService} from '../content.service';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {ControlsLightboxComponent} from './controls/controls.lightbox.gallery.component';
|
||||
import {SupportedFormats} from '../../../../../common/SupportedFormats';
|
||||
import {GridMedia} from '../grid/GridMedia';
|
||||
|
||||
export enum LightboxStates {
|
||||
Open = 1,
|
||||
@ -389,7 +390,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.router
|
||||
.navigate([], {
|
||||
queryParams: this.queryService.getParams(
|
||||
this.gridPhotoQL.toArray()[photoIndex].gridMedia.media
|
||||
this.gridPhotoQL.get(photoIndex).gridMedia.media
|
||||
),
|
||||
})
|
||||
.catch(console.error);
|
||||
@ -440,14 +441,12 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
private updateActivePhoto(photoIndex: number, resize = true): void {
|
||||
const pcList = this.gridPhotoQL.toArray();
|
||||
|
||||
if (photoIndex < 0 || photoIndex > this.gridPhotoQL.length) {
|
||||
throw new Error('Can\'t find the media');
|
||||
}
|
||||
this.videoSourceError = false;
|
||||
this.activePhotoId = photoIndex;
|
||||
this.activePhoto = pcList[photoIndex];
|
||||
this.activePhoto = this.gridPhotoQL.get(photoIndex);
|
||||
|
||||
if (resize) {
|
||||
this.animatePhoto(
|
||||
@ -455,7 +454,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
);
|
||||
}
|
||||
this.navigation.hasPrev = photoIndex > 0;
|
||||
this.navigation.hasNext = photoIndex + 1 < pcList.length;
|
||||
this.navigation.hasNext = photoIndex + 1 < this.gridPhotoQL.length;
|
||||
|
||||
const to = this.activePhoto.getDimension();
|
||||
|
||||
@ -501,5 +500,12 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
|
||||
return {top, left, width, height} as Dimension;
|
||||
}
|
||||
|
||||
get NexGridMedia(): GridMedia {
|
||||
if (this.activePhotoId + 1 < this.gridPhotoQL?.length) {
|
||||
return this.gridPhotoQL.get(this.activePhotoId + 1)?.gridMedia;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
(error)="onImageError()"
|
||||
(timeupdate)="onVideoProgress()"
|
||||
#video>
|
||||
<source [src]="gridMedia.getBestFitMediaPath()" (error)="onSourceError($event)">
|
||||
<source [src]="gridMedia.getBestFitMediaPath()" (error)="onSourceError()">
|
||||
Something went wrong.
|
||||
</video>
|
||||
|
||||
|
@ -7,11 +7,11 @@ import {
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { GridMedia } from '../../grid/GridMedia';
|
||||
import { MediaDTOUtils } from '../../../../../../common/entities/MediaDTO';
|
||||
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
|
||||
import { SupportedFormats } from '../../../../../../common/SupportedFormats';
|
||||
import { Config } from '../../../../../../common/config/public/Config';
|
||||
import {GridMedia} from '../../grid/GridMedia';
|
||||
import {MediaDTOUtils} from '../../../../../../common/entities/MediaDTO';
|
||||
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
|
||||
import {SupportedFormats} from '../../../../../../common/SupportedFormats';
|
||||
import {Config} from '../../../../../../common/config/public/Config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-lightbox-media',
|
||||
@ -20,42 +20,50 @@ import { Config } from '../../../../../../common/config/public/Config';
|
||||
})
|
||||
export class GalleryLightboxMediaComponent implements OnChanges {
|
||||
@Input() gridMedia: GridMedia;
|
||||
@Input() loadMedia = false;
|
||||
@Input() nextGridMedia: GridMedia;
|
||||
@Input() loadMedia = false; // prevents loading media
|
||||
@Input() windowAspect = 1;
|
||||
@Input() zoom = 1;
|
||||
@Input() drag = { x: 0, y: 0 };
|
||||
@Input() drag = {x: 0, y: 0};
|
||||
@Output() videoSourceError = new EventEmitter();
|
||||
|
||||
@ViewChild('video', { static: false }) video: ElementRef<HTMLVideoElement>;
|
||||
@ViewChild('video', {static: false}) video: ElementRef<HTMLVideoElement>;
|
||||
|
||||
prevGirdPhoto: GridMedia = null;
|
||||
|
||||
public imageSize = { width: 'auto', height: '100' };
|
||||
public imageLoadFinished = false;
|
||||
public imageSize = {width: 'auto', height: '100'};
|
||||
private nextImage = new Image();
|
||||
// do not skip to the next photo if not both are loaded (or resulted in an error)
|
||||
public imageLoadFinished = {
|
||||
this: false,
|
||||
next: false
|
||||
};
|
||||
thumbnailSrc: string = null;
|
||||
photo = {
|
||||
src: null as string,
|
||||
isBestFit: null as boolean,
|
||||
};
|
||||
public transcodeNeedVideos = SupportedFormats.TranscodeNeed.Videos;
|
||||
// if media not loaded, show thumbnail
|
||||
private mediaLoaded = false;
|
||||
private videoProgress = 0;
|
||||
|
||||
constructor(public elementRef: ElementRef, private sanitizer: DomSanitizer) {}
|
||||
constructor(public elementRef: ElementRef, private sanitizer: DomSanitizer) {
|
||||
}
|
||||
|
||||
get ImageTransform(): SafeStyle {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(
|
||||
'scale(' +
|
||||
this.zoom +
|
||||
') translate(calc(' +
|
||||
-50 / this.zoom +
|
||||
'% + ' +
|
||||
this.drag.x / this.zoom +
|
||||
'px), calc(' +
|
||||
-50 / this.zoom +
|
||||
'% + ' +
|
||||
this.drag.y / this.zoom +
|
||||
'px))'
|
||||
this.zoom +
|
||||
') translate(calc(' +
|
||||
-50 / this.zoom +
|
||||
'% + ' +
|
||||
this.drag.x / this.zoom +
|
||||
'px), calc(' +
|
||||
-50 / this.zoom +
|
||||
'% + ' +
|
||||
this.drag.y / this.zoom +
|
||||
'px))'
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,8 +128,12 @@ export class GalleryLightboxMediaComponent implements OnChanges {
|
||||
this.prevGirdPhoto = this.gridMedia;
|
||||
this.thumbnailSrc = null;
|
||||
this.photo.src = null;
|
||||
this.nextImage.src = "";
|
||||
this.mediaLoaded = false;
|
||||
this.imageLoadFinished = false;
|
||||
this.imageLoadFinished = {
|
||||
this: false,
|
||||
next: false
|
||||
};
|
||||
}
|
||||
this.setImageSize();
|
||||
if (
|
||||
@ -156,16 +168,18 @@ export class GalleryLightboxMediaComponent implements OnChanges {
|
||||
|
||||
onImageError(): void {
|
||||
// TODO:handle error
|
||||
this.imageLoadFinished = true;
|
||||
this.imageLoadFinished.this = true;
|
||||
console.error(
|
||||
'Error: cannot load media for lightbox url: ' +
|
||||
this.gridMedia.getBestFitMediaPath()
|
||||
this.gridMedia.getBestFitMediaPath()
|
||||
);
|
||||
this.loadNextPhoto();
|
||||
}
|
||||
|
||||
onImageLoad(): void {
|
||||
this.imageLoadFinished = true;
|
||||
this.imageLoadFinished.this = true;
|
||||
this.mediaLoaded = true;
|
||||
this.loadNextPhoto();
|
||||
}
|
||||
|
||||
public showThumbnail(): boolean {
|
||||
@ -178,11 +192,34 @@ export class GalleryLightboxMediaComponent implements OnChanges {
|
||||
);
|
||||
}
|
||||
|
||||
onSourceError($event: any): void {
|
||||
onSourceError(): void {
|
||||
this.mediaLoaded = false;
|
||||
this.videoSourceError.emit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads next photo to faster show it on navigation.
|
||||
* Called after the current photo is loaded
|
||||
* @private
|
||||
*/
|
||||
private loadNextPhoto(): void {
|
||||
if (!this.nextGridMedia || !this.loadMedia) {
|
||||
return;
|
||||
}
|
||||
// Videos do not support preloading
|
||||
if (!this.nextGridMedia.isPhoto()) {
|
||||
this.imageLoadFinished.next = true;
|
||||
return;
|
||||
}
|
||||
if (Config.Client.Media.Photo.Converting.enabled === true) {
|
||||
this.nextImage.src = this.nextGridMedia.getBestFitMediaPath();
|
||||
} else {
|
||||
this.nextImage.src = this.nextGridMedia.getMediaPath();
|
||||
}
|
||||
this.nextImage.onload = () => this.imageLoadFinished.next = true;
|
||||
|
||||
}
|
||||
|
||||
private loadPhoto(): void {
|
||||
if (!this.gridMedia || !this.loadMedia || !this.gridMedia.isPhoto()) {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user