mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
refactoring lightbox
This commit is contained in:
parent
9e6247ae57
commit
0542f0b556
@ -54,8 +54,8 @@ export class PersonManager implements IPersonManager {
|
||||
|
||||
this.samplePhotos[name] = media;
|
||||
}
|
||||
return this.samplePhotos[name];
|
||||
|
||||
return this.samplePhotos[name];
|
||||
}
|
||||
|
||||
async loadAll(): Promise<void> {
|
||||
|
@ -79,6 +79,7 @@ import {FacesService} from './ui/faces/faces.service';
|
||||
import {FaceComponent} from './ui/faces/face/face.component';
|
||||
import {VersionService} from './model/version.service';
|
||||
import { DirectoriesComponent } from './ui/gallery/directories/directories.component';
|
||||
import {ControlsLightboxComponent} from './ui/gallery/lightbox/controls/controls.lightbox.gallery.component';
|
||||
|
||||
|
||||
@Injectable()
|
||||
@ -162,6 +163,7 @@ export function translationsFactory(locale: string) {
|
||||
GalleryPhotoComponent,
|
||||
AdminComponent,
|
||||
InfoPanelLightboxComponent,
|
||||
ControlsLightboxComponent,
|
||||
RandomQueryBuilderGalleryComponent,
|
||||
// Face
|
||||
FaceComponent,
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<div body class="container">
|
||||
<ng-template [ngIf]="renderedDirGroups">
|
||||
<div class="alert alert-secondary" role="alert" *ngIf=" duplicateCount.photos >0" i18n>
|
||||
<div class="alert alert-secondary" role="alert" *ngIf=" duplicateCount.photos >0">
|
||||
Listing <strong>{{duplicateCount.pairs}}</strong> duplicates ({{duplicateCount.photos}} photos).
|
||||
</div>
|
||||
<div class="alert alert-secondary" role="alert" *ngIf=" duplicateCount.photos ==0" i18n>
|
||||
|
@ -0,0 +1,242 @@
|
||||
.lightbox {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1100; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
app-gallery-lightbox-photo {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blackCanvas {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1099; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
background-color: black;
|
||||
transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.navigation-arrow {
|
||||
width: 30px;
|
||||
min-width: 60px;
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
top: calc(50% - 50px);
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
font-size: xx-large;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.navigation-arrow span {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
#controllers-container {
|
||||
z-index: 1100;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 1.0;
|
||||
position: fixed;
|
||||
color: white;
|
||||
transition: width 0.3s ease-in-out, opacity 1s;
|
||||
-webkit-transition: width 0.3s ease-in-out, opacity 1s;
|
||||
-o-transition: width 0.3s ease-in-out, opacity 1s;
|
||||
-ms-transition: width 0.3s ease-in-out, opacity 1s;
|
||||
-moz-transition: width 0.3s ease-in-out, opacity 1s;
|
||||
}
|
||||
|
||||
#controllers-container.dim-controls {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
#controllers-container.dim-controls-video {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#swipeable-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#rightArrow {
|
||||
right: 0;
|
||||
left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.controls {
|
||||
height: initial;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.controls .control-button {
|
||||
margin-left: 0.2em;
|
||||
margin-right: 0.2em;
|
||||
display: inline-block;
|
||||
padding: 0 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.controls .button-disabled {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.controls-top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.controls-caption {
|
||||
opacity: 0.5;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
height: initial;
|
||||
text-align: left;
|
||||
width: 75%;
|
||||
padding: 5px 13px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.controls-playback {
|
||||
padding-right: 15px;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.controls-video {
|
||||
padding-right: 15px;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.controls-video .oi,
|
||||
.controls-video input {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.controls-big-play span {
|
||||
font-size: 20vh;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.controls-video input[type=range] {
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.controls-video .oi {
|
||||
text-align: center;
|
||||
max-width: 45px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
opacity: 0.5;
|
||||
transition: opacity .2s ease-out;
|
||||
-moz-transition: opacity .2s ease-out;
|
||||
-webkit-transition: opacity .2s ease-out;
|
||||
-o-transition: opacity .2s ease-out;
|
||||
-ms-transition: opacity .2s ease-out;
|
||||
}
|
||||
|
||||
.highlight:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
@-webkit-keyframes blink {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
25% {
|
||||
opacity: 1.0;
|
||||
}
|
||||
75% {
|
||||
opacity: 1.0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.button-active {
|
||||
animation: blink 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
app-info-panel {
|
||||
z-index: 1100; /* Sit on top */
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
max-width: 100vw;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.controls-zoom {
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.zoom-progress-wrapper {
|
||||
margin-top: 3px;
|
||||
height: 11px;
|
||||
border: 1px solid white;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
input[type="range"].zoom-progress {
|
||||
height: 11px;
|
||||
margin-top: 3px;
|
||||
border: 1px solid white;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="range"].zoom-progress::-webkit-slider-runnable-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
input[type="range"].zoom-progress::-moz-range-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.controls-zoom .oi {
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
<div id="controllers-container" #root>
|
||||
<div class="controls-caption" *ngIf="Title">{{Title}}</div>
|
||||
<div class="controls controls-top">
|
||||
<a class="highlight control-button"
|
||||
*ngIf="activePhoto"
|
||||
[href]="activePhoto.gridPhoto.getPhotoPath()"
|
||||
[download]="activePhoto.gridPhoto.media.name">
|
||||
<span class="oi oi-data-transfer-download"
|
||||
title="download" i18n-title></span>
|
||||
</a>
|
||||
|
||||
<div class=" highlight control-button" (click)="toggleInfoPanel.emit()"
|
||||
title="info key: i" i18n-title>
|
||||
<span class="oi oi-info"></span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="fullScreenService.isFullScreenEnabled()"
|
||||
class=" highlight control-button"
|
||||
(click)="toggleFullScreen.emit()"
|
||||
title="toggle fullscreen, key: f" i18n-title>
|
||||
<span class="oi oi-fullscreen-exit">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!fullScreenService.isFullScreenEnabled()"
|
||||
class="highlight control-button"
|
||||
(click)="toggleFullScreen.emit(true)"
|
||||
title="toggle fullscreen, key: f" i18n-title>
|
||||
<span class="oi oi-fullscreen-enter">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="highlight control-button"
|
||||
(click)="closed.emit()"
|
||||
title="close, key: Escape" i18n-title>
|
||||
<span class="oi oi-x">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="swipeable-container"
|
||||
(swipeleft)="zoom == 1 && nextPhoto.emit()"
|
||||
(swiperight)="zoom == 1 && previousPhoto.emit()"
|
||||
(swipeup)="zoom == 1 && closed.emit()"
|
||||
(tap)="tap($event)"
|
||||
(pan)="pan($event)"
|
||||
(wheel)="wheel($event)"
|
||||
(click)="mediaElement.playPause()">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="navigation-arrow highlight"
|
||||
*ngIf="navigation.hasPrev && zoom == 1" title="key: left arrow" id="leftArrow" i18n-title
|
||||
(click)="previousPhoto.emit()"><span
|
||||
class="oi oi-chevron-left"></span></div>
|
||||
<div class="navigation-arrow highlight"
|
||||
*ngIf="navigation.hasNext && zoom == 1" title="key: right arrow" id="rightArrow" i18n-title
|
||||
(click)="nextPhoto.emit()"><span
|
||||
class="oi oi-chevron-right"></span></div>
|
||||
|
||||
<div class="controls controls-zoom row" *ngIf="Zoom > 1">
|
||||
<div class="col-1 col-md-4">
|
||||
<span (click)="zoomOut()" i18n-title title="Zoom out, key: '-'"
|
||||
class="oi oi-zoom-out float-right"></span>
|
||||
</div>
|
||||
<input type="range"
|
||||
[(ngModel)]="Zoom" min="1" [max]="MAX_ZOOM" step="0.1"
|
||||
value="1" class="col-10 col-md-4 zoom-progress">
|
||||
<div class="col-1 col-md-4">
|
||||
<span (click)="zoomIn()" i18n-title title="Zoom in, key: '+'"
|
||||
class="oi oi-zoom-in float-left"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="controls controls-playback"
|
||||
*ngIf="zoom == 1 && activePhoto && activePhoto.gridPhoto.isPhoto()">
|
||||
<span class="oi oi-media-pause highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.Paused ? 'button-disabled':''"
|
||||
(click)="pause()"
|
||||
title="pause"></span>
|
||||
<span
|
||||
class="oi oi-media-play highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.Play ? 'button-active':''"
|
||||
(click)="play()"
|
||||
title="auto play"></span>
|
||||
<span class="oi oi-media-skip-forward highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.FastForward ? 'button-active':''"
|
||||
(click)="fastForward()"
|
||||
title="fast auto play"></span>
|
||||
</div>
|
||||
|
||||
<div class="controls controls-big-play"
|
||||
*ngIf="activePhoto && activePhoto.gridPhoto.isVideo() && mediaElement.Paused">
|
||||
<span class="oi oi-media-play"></span>
|
||||
</div>
|
||||
|
||||
<div class="controls controls-video row" *ngIf="activePhoto && activePhoto.gridPhoto.isVideo()">
|
||||
<span class="oi col-1"
|
||||
[ngClass]="!mediaElement.Paused ? 'oi-media-pause':'oi-media-play'"
|
||||
(click)="mediaElement.playPause()"></span>
|
||||
<input type="range" [(ngModel)]="mediaElement.VideoProgress"
|
||||
min="0" max="100" step="0.1" class="col video-progress">
|
||||
<span class="oi col-1"
|
||||
[ngClass]="mediaElement.Muted ? 'oi-volume-off':'oi-volume-high'"
|
||||
(click)="mediaElement.mute()"></span>
|
||||
<input type="range"
|
||||
[(ngModel)]="mediaElement.VideoVolume" min="0" max="1" step="0.1"
|
||||
value="1" class="col-2 col-md-1 volume">
|
||||
</div>
|
||||
<!-- <div class="faces"
|
||||
*ngIf="activePhoto">
|
||||
<div
|
||||
[style.top.%]="face.box.top / activePhoto.gridPhoto.Photo.metadata.size.height*100"
|
||||
[style.left.%]="face.box.left / activePhoto.gridPhoto.Photo.metadata.size.width*100"
|
||||
[style.height.%]="face.box.height / activePhoto.gridPhoto.Photo.metadata.size.height*100"
|
||||
[style.width.%]="face.box.width / activePhoto.gridPhoto.Photo.metadata.size.width*100"
|
||||
style="background-color: red"
|
||||
*ngFor="let face of activePhoto.gridPhoto.Photo.metadata.faces">
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
@ -0,0 +1,343 @@
|
||||
import {Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
import {MediaDTO} 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';
|
||||
|
||||
export enum PlayBackStates {
|
||||
Paused = 1,
|
||||
Play = 2,
|
||||
FastForward = 3
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-lightbox-controls',
|
||||
styleUrls: ['./controls.lightbox.gallery.component.css', './inputrange.css'],
|
||||
templateUrl: './controls.lightbox.gallery.component.html',
|
||||
})
|
||||
export class ControlsLightboxComponent implements OnDestroy, OnInit {
|
||||
|
||||
readonly MAX_ZOOM = 10;
|
||||
|
||||
@Output() closed = new EventEmitter();
|
||||
@Output() toggleInfoPanel = new EventEmitter();
|
||||
@Output() toggleFullScreen = new EventEmitter();
|
||||
@Output() nextPhoto = new EventEmitter();
|
||||
@Output() previousPhoto = new EventEmitter();
|
||||
|
||||
@Input() navigation = {hasPrev: true, hasNext: true};
|
||||
@Input() activePhoto: GalleryPhotoComponent;
|
||||
@Input() mediaElement: GalleryLightboxMediaComponent;
|
||||
@Input() photoFrameDim = {width: 1, height: 1, aspect: 1};
|
||||
|
||||
|
||||
public zoom = 1;
|
||||
public playBackState: PlayBackStates = PlayBackStates.Paused;
|
||||
public PlayBackStates = PlayBackStates;
|
||||
public controllersDimmed = false;
|
||||
public controllersAlwaysOn = false;
|
||||
public controllersVisible = true;
|
||||
visibilityTimer: number = null;
|
||||
// delayedMediaShow: string = null;
|
||||
public drag = {x: 0, y: 0};
|
||||
|
||||
private timer: Observable<number>;
|
||||
private timerSub: Subscription;
|
||||
private prevDrag = {x: 0, y: 0};
|
||||
private prevZoom = 1;
|
||||
|
||||
constructor(public fullScreenService: FullScreenService) {
|
||||
}
|
||||
|
||||
public get Zoom(): number {
|
||||
return this.zoom;
|
||||
}
|
||||
|
||||
public set Zoom(zoom: number) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if (zoom < 1) {
|
||||
zoom = 1;
|
||||
}
|
||||
if (zoom > this.MAX_ZOOM) {
|
||||
zoom = this.MAX_ZOOM;
|
||||
}
|
||||
if (this.zoom === zoom) {
|
||||
return;
|
||||
}
|
||||
this.pause();
|
||||
this.drag.x = this.drag.x / this.zoom * zoom;
|
||||
this.drag.y = this.drag.y / this.zoom * zoom;
|
||||
this.prevDrag.x = this.drag.x;
|
||||
this.prevDrag.y = this.drag.y;
|
||||
this.zoom = zoom;
|
||||
this.checkZoomAndDrag();
|
||||
}
|
||||
|
||||
get Title(): string {
|
||||
if (!this.activePhoto) {
|
||||
return null;
|
||||
}
|
||||
return (<PhotoDTO>this.activePhoto.gridPhoto.media).metadata.caption;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.timer = timer(1000, 2000);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.pause();
|
||||
|
||||
if (this.visibilityTimer != null) {
|
||||
clearTimeout(this.visibilityTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pan($event: { deltaY: number, deltaX: number, isFinal: boolean }) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if (this.zoom === 1) {
|
||||
return;
|
||||
}
|
||||
this.drag.x = this.prevDrag.x + $event.deltaX;
|
||||
this.drag.y = this.prevDrag.y + $event.deltaY;
|
||||
this.checkZoomAndDrag();
|
||||
if ($event.isFinal) {
|
||||
this.prevDrag = {
|
||||
x: this.drag.x,
|
||||
y: this.drag.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wheel($event: { deltaY: number }) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if ($event.deltaY < 0) {
|
||||
this.zoomIn();
|
||||
} else {
|
||||
this.zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('pinch', ['$event'])
|
||||
pinch($event: { scale: number }) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
this.showControls();
|
||||
this.Zoom = this.prevZoom * $event.scale;
|
||||
}
|
||||
|
||||
@HostListener('pinchend', ['$event'])
|
||||
pinchend($event: { scale: number }) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
this.showControls();
|
||||
this.Zoom = this.prevZoom * $event.scale;
|
||||
this.prevZoom = this.zoom;
|
||||
}
|
||||
|
||||
tap($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if ($event.tapCount < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showControls();
|
||||
if (this.zoom > 1) {
|
||||
this.Zoom = 1;
|
||||
this.prevZoom = this.zoom;
|
||||
return;
|
||||
} else {
|
||||
this.Zoom = 5;
|
||||
this.prevZoom = this.zoom;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.showControls();
|
||||
this.Zoom = this.zoom + this.zoom / 10;
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.showControls();
|
||||
this.Zoom = this.zoom - this.zoom / 10;
|
||||
}
|
||||
|
||||
|
||||
@HostListener('window:keydown', ['$event'])
|
||||
onKeyPress(e: KeyboardEvent) {
|
||||
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
||||
switch (event.key) {
|
||||
case 'ArrowLeft':
|
||||
if (this.navigation.hasPrev) {
|
||||
this.previousPhoto.emit();
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
if (this.navigation.hasNext) {
|
||||
this.nextPhoto.emit();
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
this.toggleInfoPanel.emit();
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
this.toggleFullScreen.emit();
|
||||
break;
|
||||
case '-':
|
||||
this.zoomOut();
|
||||
break;
|
||||
case '+':
|
||||
this.zoomIn();
|
||||
break;
|
||||
case 'c':
|
||||
case 'C':
|
||||
this.controllersAlwaysOn = !this.controllersAlwaysOn;
|
||||
break;
|
||||
case 'Escape': // escape
|
||||
this.closed.emit();
|
||||
break;
|
||||
case ' ': // space
|
||||
if (this.activePhoto && this.activePhoto.gridPhoto.isVideo()) {
|
||||
this.mediaElement.playPause();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public play() {
|
||||
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.gridPhoto.isVideo() &&
|
||||
!this.mediaElement.Paused) {
|
||||
return;
|
||||
}
|
||||
this.nextPhoto.emit();
|
||||
});
|
||||
this.playBackState = PlayBackStates.Play;
|
||||
}
|
||||
|
||||
|
||||
public fastForward() {
|
||||
this.pause();
|
||||
this.timerSub = this.timer.subscribe(() => {
|
||||
if (this.mediaElement.imageLoadFinished === false) {
|
||||
return;
|
||||
}
|
||||
if (this.activePhoto && this.activePhoto.gridPhoto.isVideo() &&
|
||||
!this.mediaElement.Paused) {
|
||||
return;
|
||||
}
|
||||
this.nextPhoto.emit();
|
||||
});
|
||||
this.playBackState = PlayBackStates.FastForward;
|
||||
}
|
||||
|
||||
@HostListener('mousemove')
|
||||
onMouseMove() {
|
||||
this.showControls();
|
||||
}
|
||||
|
||||
public pause() {
|
||||
if (this.timerSub != null) {
|
||||
this.timerSub.unsubscribe();
|
||||
}
|
||||
this.playBackState = PlayBackStates.Paused;
|
||||
}
|
||||
|
||||
|
||||
resetZoom() {
|
||||
this.Zoom = 1;
|
||||
}
|
||||
|
||||
onResize() {
|
||||
this.checkZoomAndDrag();
|
||||
}
|
||||
|
||||
private checkZoomAndDrag() {
|
||||
const fixDrag = (drag: { x: number, y: number }) => {
|
||||
if (this.zoom === 1) {
|
||||
drag.y = 0;
|
||||
drag.x = 0;
|
||||
return;
|
||||
}
|
||||
if (!this.activePhoto) {
|
||||
return;
|
||||
}
|
||||
|
||||
const photoAspect = MediaDTO.calcRotatedAspectRatio(this.activePhoto.gridPhoto.media);
|
||||
const widthFilled = photoAspect > this.photoFrameDim.aspect;
|
||||
const divWidth = this.photoFrameDim.width;
|
||||
const divHeight = this.photoFrameDim.height;
|
||||
const size = {
|
||||
width: (widthFilled ? divWidth : divHeight * photoAspect) * this.zoom,
|
||||
height: (widthFilled ? divWidth / photoAspect : divHeight) * this.zoom
|
||||
};
|
||||
|
||||
|
||||
const widthDrag = Math.abs(divWidth - size.width) / 2;
|
||||
const heightDrag = Math.abs(divHeight - size.height) / 2;
|
||||
|
||||
if (divWidth > size.width) {
|
||||
drag.x = 0;
|
||||
}
|
||||
if (divHeight > size.height) {
|
||||
drag.y = 0;
|
||||
}
|
||||
|
||||
if (drag.x < -widthDrag) {
|
||||
drag.x = -widthDrag;
|
||||
}
|
||||
if (drag.x > widthDrag) {
|
||||
drag.x = widthDrag;
|
||||
}
|
||||
if (drag.y < -heightDrag) {
|
||||
drag.y = -heightDrag;
|
||||
}
|
||||
if (drag.y > heightDrag) {
|
||||
drag.y = heightDrag;
|
||||
}
|
||||
};
|
||||
if (this.zoom < 1) {
|
||||
this.zoom = 1;
|
||||
}
|
||||
if (this.zoom > this.MAX_ZOOM) {
|
||||
this.zoom = this.MAX_ZOOM;
|
||||
}
|
||||
fixDrag(this.drag);
|
||||
fixDrag(this.prevDrag);
|
||||
}
|
||||
|
||||
private showControls() {
|
||||
this.controllersDimmed = false;
|
||||
if (this.visibilityTimer != null) {
|
||||
clearTimeout(this.visibilityTimer);
|
||||
}
|
||||
this.visibilityTimer = window.setTimeout(this.hideControls, 2000);
|
||||
}
|
||||
|
||||
private hideControls = () => {
|
||||
this.controllersDimmed = true;
|
||||
};
|
||||
}
|
||||
|
@ -7,128 +7,25 @@
|
||||
<div class="lightbox" #lightbox>
|
||||
<app-gallery-lightbox-media [gridMedia]="activePhoto ? activePhoto.gridPhoto : null"
|
||||
[loadMedia]="!animating"
|
||||
[zoom]="zoom"
|
||||
[drag]="drag"
|
||||
[windowAspect]="getWindowAspectRatio()"
|
||||
[zoom]="controls ? controls.Zoom : 1"
|
||||
[drag]="controls ? controls.drag : {x:0,y:0}"
|
||||
[windowAspect]="photoFrameDim.aspect"
|
||||
#photo>
|
||||
</app-gallery-lightbox-media>
|
||||
<app-lightbox-controls
|
||||
#controls
|
||||
[activePhoto]="activePhoto"
|
||||
(closed)="hide()"
|
||||
[navigation]="navigation"
|
||||
(nextPhoto)="nextImage()"
|
||||
(previousPhoto)="prevImage()"
|
||||
(toggleInfoPanel)="toggleInfoPanel()"
|
||||
(toggleFullScreen)="toggleFullscreen()"
|
||||
[photoFrameDim]="photoFrameDim"
|
||||
[mediaElement]="mediaElement">
|
||||
</app-lightbox-controls>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="controllersVisible"
|
||||
id="controllers-container"
|
||||
#controls
|
||||
[style.width.px]="getPhotoFrameWidth()"
|
||||
[ngClass]="(controllersDimmed && !controllersAlwaysOn) ? (activePhoto && activePhoto.gridPhoto.isVideo() ? 'dim-controls-video' :'dim-controls'): ''">
|
||||
<div class="controls-caption" *ngIf="Title">{{Title}}</div>
|
||||
<div class="controls controls-top">
|
||||
<a *ngIf="activePhoto"
|
||||
class="highlight control-button"
|
||||
[href]="activePhoto.gridPhoto.getPhotoPath()"
|
||||
[download]="activePhoto.gridPhoto.media.name">
|
||||
<span class="oi oi-data-transfer-download"
|
||||
title="download" i18n-title></span>
|
||||
</a>
|
||||
|
||||
<div class=" highlight control-button" (click)="toggleInfoPanel()"
|
||||
title="info key: i" i18n-title>
|
||||
<span class="oi oi-info"></span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="fullScreenService.isFullScreenEnabled()"
|
||||
class=" highlight control-button"
|
||||
(click)="fullScreenService.exitFullScreen()"
|
||||
title="toggle fullscreen, key: f" i18n-title>
|
||||
<span class="oi oi-fullscreen-exit">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!fullScreenService.isFullScreenEnabled()"
|
||||
class="highlight control-button"
|
||||
(click)="fullScreenService.showFullScreen(root)"
|
||||
title="toggle fullscreen, key: f" i18n-title>
|
||||
<span class="oi oi-fullscreen-enter">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="highlight control-button"
|
||||
(click)="hide()"
|
||||
title="close, key: Escape" i18n-title>
|
||||
<span class="oi oi-x">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="swipeable-container"
|
||||
(swipeleft)="zoom == 1 && nextImage()"
|
||||
(swiperight)="zoom == 1 && prevImage()"
|
||||
(swipeup)="zoom == 1 && hide()"
|
||||
(tap)="tap($event)"
|
||||
(pan)="pan($event)"
|
||||
(wheel)="wheel($event)"
|
||||
(click)="mediaElement.playPause()">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="navigation-arrow highlight"
|
||||
*ngIf="navigation.hasPrev && zoom == 1" title="key: left arrow" id="leftArrow" i18n-title
|
||||
(click)="prevImage()"><span
|
||||
class="oi oi-chevron-left"></span></div>
|
||||
<div class="navigation-arrow highlight"
|
||||
*ngIf="navigation.hasNext && zoom == 1" title="key: right arrow" id="rightArrow" i18n-title
|
||||
(click)="nextImage()"><span
|
||||
class="oi oi-chevron-right"></span></div>
|
||||
|
||||
<div class="controls controls-zoom row" *ngIf="zoom > 1">
|
||||
<div class="col-1 col-md-4">
|
||||
<span (click)="zoomOut()" i18n-title title="Zoom out, key: '-'"
|
||||
class="oi oi-zoom-out float-right"></span>
|
||||
</div>
|
||||
<input type="range"
|
||||
[(ngModel)]="Zoom" min="1" [max]="MAX_ZOOM" step="0.1"
|
||||
value="1" class="col-10 col-md-4 zoom-progress">
|
||||
<div class="col-1 col-md-4">
|
||||
<span (click)="zoomIn()" i18n-title title="Zoom in, key: '+'"
|
||||
class="oi oi-zoom-in float-left"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls controls-playback"
|
||||
*ngIf="zoom == 1 && activePhoto && activePhoto.gridPhoto.isPhoto()">
|
||||
<span class="oi oi-media-pause highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.Paused ? 'button-disabled':''"
|
||||
(click)="pause()"
|
||||
title="pause"></span>
|
||||
<span
|
||||
class="oi oi-media-play highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.Play ? 'button-active':''"
|
||||
(click)="play()"
|
||||
title="auto play"></span>
|
||||
<span class="oi oi-media-skip-forward highlight control-button"
|
||||
[ngClass]="playBackState == PlayBackStates.FastForward ? 'button-active':''"
|
||||
(click)="fastForward()"
|
||||
title="fast auto play"></span>
|
||||
</div>
|
||||
|
||||
<div class="controls controls-big-play"
|
||||
*ngIf="activePhoto && activePhoto.gridPhoto.isVideo() && mediaElement.Paused">
|
||||
<span class="oi oi-media-play"></span>
|
||||
</div>
|
||||
|
||||
<div class="controls controls-video row" *ngIf="activePhoto && activePhoto.gridPhoto.isVideo()">
|
||||
<span class="oi col-1"
|
||||
[ngClass]="!mediaElement.Paused ? 'oi-media-pause':'oi-media-play'"
|
||||
(click)="mediaElement.playPause()"></span>
|
||||
<input type="range" [(ngModel)]="mediaElement.VideoProgress"
|
||||
min="0" max="100" step="0.1" class="col video-progress">
|
||||
<span class="oi col-1"
|
||||
[ngClass]="mediaElement.Muted ? 'oi-volume-off':'oi-volume-high'"
|
||||
(click)="mediaElement.mute()"></span>
|
||||
<input type="range"
|
||||
[(ngModel)]="mediaElement.VideoVolume" min="0" max="1" step="0.1"
|
||||
value="1" class="col-2 col-md-1 volume">
|
||||
</div>
|
||||
</div>
|
||||
<app-info-panel *ngIf="activePhoto && infoPanelVisible"
|
||||
id="info-panel"
|
||||
[style.width.px]="infoPanelWidth"
|
||||
|
@ -1,21 +1,11 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
QueryList,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, ElementRef, HostListener, OnDestroy, OnInit, QueryList, ViewChild} from '@angular/core';
|
||||
import {GalleryPhotoComponent} from '../grid/photo/photo.grid.gallery.component';
|
||||
import {Dimension} from '../../../model/IRenderable';
|
||||
import {FullScreenService} from '../fullscreen.service';
|
||||
import {OverlayService} from '../overlay.service';
|
||||
import {animate, AnimationBuilder, AnimationPlayer, style} from '@angular/animations';
|
||||
import {GalleryLightboxMediaComponent} from './media/media.lightbox.gallery.component';
|
||||
import {Observable, Subscription, timer} from 'rxjs';
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||
import {PageHelper} from '../../../model/page.helper';
|
||||
import {QueryService} from '../../../model/query.service';
|
||||
@ -23,6 +13,7 @@ import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
||||
import {QueryParams} from '../../../../../common/QueryParams';
|
||||
import {GalleryService} from '../gallery.service';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {ControlsLightboxComponent} from './controls/controls.lightbox.gallery.component';
|
||||
|
||||
export enum LightboxStates {
|
||||
Open = 1,
|
||||
@ -30,48 +21,22 @@ export enum LightboxStates {
|
||||
Closed = 3
|
||||
}
|
||||
|
||||
export enum PlayBackStates {
|
||||
Paused = 1,
|
||||
Play = 2,
|
||||
FastForward = 3
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-gallery-lightbox',
|
||||
styleUrls: ['./lightbox.gallery.component.css', './inputrange.css'],
|
||||
styleUrls: ['./lightbox.gallery.component.css'],
|
||||
templateUrl: './lightbox.gallery.component.html'
|
||||
})
|
||||
export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
|
||||
readonly MAX_ZOOM = 10;
|
||||
|
||||
@ViewChild('photo') mediaElement: GalleryLightboxMediaComponent;
|
||||
@ViewChild('controls') controls: ControlsLightboxComponent;
|
||||
@ViewChild('lightbox') lightboxElement: ElementRef;
|
||||
@ViewChild('root') root: ElementRef;
|
||||
|
||||
public navigation = {hasPrev: true, hasNext: true};
|
||||
public blackCanvasOpacity = 0;
|
||||
|
||||
private activePhotoId: number = null;
|
||||
public activePhoto: GalleryPhotoComponent;
|
||||
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||
|
||||
public status: LightboxStates = LightboxStates.Closed;
|
||||
private subscription: {
|
||||
photosChange: Subscription,
|
||||
route: Subscription
|
||||
} = {
|
||||
photosChange: null,
|
||||
route: null
|
||||
};
|
||||
private timer: Observable<number>;
|
||||
private timerSub: Subscription;
|
||||
public playBackState: PlayBackStates = PlayBackStates.Paused;
|
||||
public PlayBackStates = PlayBackStates;
|
||||
public controllersDimmed = false;
|
||||
public controllersAlwaysOn = false;
|
||||
public controllersVisible = true;
|
||||
|
||||
public infoPanelVisible = false;
|
||||
public infoPanelWidth = 0;
|
||||
public animating = false;
|
||||
@ -79,10 +44,17 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
iPvisibilityTimer: number = null;
|
||||
visibilityTimer: number = null;
|
||||
delayedMediaShow: string = null;
|
||||
public zoom = 1;
|
||||
public drag = {x: 0, y: 0};
|
||||
private prevDrag = {x: 0, y: 0};
|
||||
private prevZoom = 1;
|
||||
public photoFrameDim = {width: 1, height: 1, aspect: 1};
|
||||
private activePhotoId: number = null;
|
||||
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||
private subscription: {
|
||||
photosChange: Subscription,
|
||||
route: Subscription
|
||||
} = {
|
||||
photosChange: null,
|
||||
route: null
|
||||
};
|
||||
|
||||
|
||||
constructor(public fullScreenService: FullScreenService,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
@ -94,8 +66,24 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
|
||||
get Title(): string {
|
||||
if (!this.activePhoto) {
|
||||
return null;
|
||||
}
|
||||
return (<PhotoDTO>this.activePhoto.gridPhoto.media).metadata.caption;
|
||||
}
|
||||
|
||||
public toggleFullscreen(): void {
|
||||
if (this.fullScreenService.isFullScreenEnabled()) {
|
||||
this.fullScreenService.exitFullScreen();
|
||||
} else {
|
||||
this.fullScreenService.showFullScreen(this.root.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.timer = timer(1000, 2000);
|
||||
this.updatePhotoFrameDim();
|
||||
this.subscription.route = this.route.queryParams.subscribe((params: Params) => {
|
||||
if (params[QueryParams.gallery.photo] && params[QueryParams.gallery.photo] !== '') {
|
||||
if (!this.gridPhotoQL) {
|
||||
@ -109,9 +97,10 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.pause();
|
||||
if (this.controls) {
|
||||
this.controls.pause();
|
||||
}
|
||||
if (this.subscription.photosChange != null) {
|
||||
this.subscription.photosChange.unsubscribe();
|
||||
}
|
||||
@ -132,7 +121,9 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Zoom = 1;
|
||||
if (this.controls) {
|
||||
this.controls.resetZoom();
|
||||
}
|
||||
const photo = this.gridPhotoQL.find(i => this.queryService.getMediaStringId(i.gridPhoto.media) === photoStringId);
|
||||
if (!photo) {
|
||||
return this.delayedMediaShow = photoStringId;
|
||||
@ -164,165 +155,6 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
pan($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if (this.zoom === 1) {
|
||||
return;
|
||||
}
|
||||
this.drag.x = this.prevDrag.x + $event.deltaX;
|
||||
this.drag.y = this.prevDrag.y + $event.deltaY;
|
||||
this.checkZoomAndDrag();
|
||||
if ($event.isFinal) {
|
||||
this.prevDrag = {
|
||||
x: this.drag.x,
|
||||
y: this.drag.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wheel($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if ($event.deltaY < 0) {
|
||||
this.zoomIn();
|
||||
} else {
|
||||
this.zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('pinch', ['$event'])
|
||||
pinch($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
this.showControls();
|
||||
this.Zoom = this.prevZoom * $event.scale;
|
||||
}
|
||||
|
||||
@HostListener('pinchend', ['$event'])
|
||||
pinchend($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
this.showControls();
|
||||
this.Zoom = this.prevZoom * $event.scale;
|
||||
this.prevZoom = this.zoom;
|
||||
}
|
||||
|
||||
tap($event: any) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if ($event.tapCount < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showControls();
|
||||
if (this.zoom > 1) {
|
||||
this.Zoom = 1;
|
||||
this.prevZoom = this.zoom;
|
||||
return;
|
||||
} else {
|
||||
this.Zoom = 5;
|
||||
this.prevZoom = this.zoom;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.showControls();
|
||||
this.Zoom = this.zoom + this.zoom / 10;
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.showControls();
|
||||
this.Zoom = this.zoom - this.zoom / 10;
|
||||
}
|
||||
|
||||
|
||||
public get Zoom(): number {
|
||||
return this.zoom;
|
||||
}
|
||||
|
||||
public set Zoom(zoom: number) {
|
||||
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||
return;
|
||||
}
|
||||
if (zoom < 1) {
|
||||
zoom = 1;
|
||||
}
|
||||
if (zoom > this.MAX_ZOOM) {
|
||||
zoom = this.MAX_ZOOM;
|
||||
}
|
||||
if (this.zoom === zoom) {
|
||||
return;
|
||||
}
|
||||
this.pause();
|
||||
this.drag.x = this.drag.x / this.zoom * zoom;
|
||||
this.drag.y = this.drag.y / this.zoom * zoom;
|
||||
this.prevDrag.x = this.drag.x;
|
||||
this.prevDrag.y = this.drag.y;
|
||||
this.zoom = zoom;
|
||||
this.checkZoomAndDrag();
|
||||
}
|
||||
|
||||
private checkZoomAndDrag() {
|
||||
const fixDrag = (drag: { x: number, y: number }) => {
|
||||
if (this.zoom === 1) {
|
||||
drag.y = 0;
|
||||
drag.x = 0;
|
||||
return;
|
||||
}
|
||||
if (!this.activePhoto) {
|
||||
return;
|
||||
}
|
||||
|
||||
const photoAspect = MediaDTO.calcRotatedAspectRatio(this.activePhoto.gridPhoto.media);
|
||||
const widthFilled = photoAspect > this.getWindowAspectRatio();
|
||||
const divWidth = this.getPhotoFrameWidth();
|
||||
const divHeight = this.getPhotoFrameHeight();
|
||||
const size = {
|
||||
width: (widthFilled ? divWidth : divHeight * photoAspect) * this.zoom,
|
||||
height: (widthFilled ? divWidth / photoAspect : divHeight) * this.zoom
|
||||
};
|
||||
|
||||
|
||||
const widthDrag = Math.abs(divWidth - size.width) / 2;
|
||||
const heightDrag = Math.abs(divHeight - size.height) / 2;
|
||||
|
||||
if (divWidth > size.width) {
|
||||
drag.x = 0;
|
||||
}
|
||||
if (divHeight > size.height) {
|
||||
drag.y = 0;
|
||||
}
|
||||
|
||||
if (drag.x < -widthDrag) {
|
||||
drag.x = -widthDrag;
|
||||
}
|
||||
if (drag.x > widthDrag) {
|
||||
drag.x = widthDrag;
|
||||
}
|
||||
if (drag.y < -heightDrag) {
|
||||
drag.y = -heightDrag;
|
||||
}
|
||||
if (drag.y > heightDrag) {
|
||||
drag.y = heightDrag;
|
||||
}
|
||||
};
|
||||
if (this.zoom < 1) {
|
||||
this.zoom = 1;
|
||||
}
|
||||
if (this.zoom > this.MAX_ZOOM) {
|
||||
this.zoom = this.MAX_ZOOM;
|
||||
}
|
||||
fixDrag(this.drag);
|
||||
fixDrag(this.prevDrag);
|
||||
}
|
||||
|
||||
//noinspection JSUnusedGlobalSymbols
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize() {
|
||||
@ -330,37 +162,30 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.animateLightbox();
|
||||
this.updateActivePhoto(this.activePhotoId);
|
||||
}
|
||||
this.updatePhotoFrameDim();
|
||||
}
|
||||
|
||||
public nextImage() {
|
||||
if (this.activePhotoId + 1 < this.gridPhotoQL.length) {
|
||||
this.navigateToPhoto(this.activePhotoId + 1);
|
||||
} else {
|
||||
this.navigateToPhoto(0);
|
||||
}
|
||||
}
|
||||
|
||||
public prevImage() {
|
||||
this.pause();
|
||||
if (this.controls) {
|
||||
this.controls.pause();
|
||||
}
|
||||
if (this.activePhotoId > 0) {
|
||||
this.navigateToPhoto(this.activePhotoId - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private navigateToPhoto(photoIndex: number) {
|
||||
this.router.navigate([],
|
||||
{queryParams: this.queryService.getParams(this.gridPhotoQL.toArray()[photoIndex].gridPhoto.media)}).catch(console.error);
|
||||
}
|
||||
|
||||
private showPhoto(photoIndex: number, resize: boolean = true) {
|
||||
this.activePhoto = null;
|
||||
this.changeDetector.detectChanges();
|
||||
this.updateActivePhoto(photoIndex, resize);
|
||||
}
|
||||
|
||||
public showLigthbox(photo: MediaDTO) {
|
||||
this.Zoom = 1;
|
||||
this.controllersVisible = true;
|
||||
this.showControls();
|
||||
if (this.controls) {
|
||||
this.controls.resetZoom();
|
||||
}
|
||||
this.status = LightboxStates.Open;
|
||||
const selectedPhoto = this.findPhotoComponent(photo);
|
||||
if (selectedPhoto === null) {
|
||||
@ -378,8 +203,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth(),
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: this.photoFrameDim.width,
|
||||
height: this.photoFrameDim.height
|
||||
});
|
||||
|
||||
|
||||
@ -391,99 +216,11 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.showPhoto(this.gridPhotoQL.toArray().indexOf(selectedPhoto), false);
|
||||
}
|
||||
|
||||
@HostListener('window:keydown', ['$event'])
|
||||
onKeyPress(e: KeyboardEvent) {
|
||||
if (this.status !== LightboxStates.Open) {
|
||||
return;
|
||||
}
|
||||
const event: KeyboardEvent = window.event ? <any>window.event : e;
|
||||
switch (event.key) {
|
||||
case 'ArrowLeft':
|
||||
if (this.activePhotoId > 0) {
|
||||
this.prevImage();
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
if (this.activePhotoId < this.gridPhotoQL.length - 1) {
|
||||
this.nextImage();
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
if (this.isInfoPanelAnimating()) {
|
||||
return;
|
||||
}
|
||||
if (this.infoPanelVisible) {
|
||||
this.hideInfoPanel(true);
|
||||
} else {
|
||||
this.showInfoPanel();
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
if (this.fullScreenService.isFullScreenEnabled()) {
|
||||
this.fullScreenService.exitFullScreen();
|
||||
} else {
|
||||
this.fullScreenService.showFullScreen(this.root.nativeElement);
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
this.zoomOut();
|
||||
break;
|
||||
case '+':
|
||||
this.zoomIn();
|
||||
break;
|
||||
case 'c':
|
||||
case 'C':
|
||||
this.controllersAlwaysOn = !this.controllersAlwaysOn;
|
||||
break;
|
||||
case 'Escape': // escape
|
||||
this.hide();
|
||||
break;
|
||||
case ' ': // space
|
||||
if (this.activePhoto && this.activePhoto.gridPhoto.isVideo()) {
|
||||
this.mediaElement.playPause();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.router.navigate([],
|
||||
{queryParams: this.queryService.getParams()}).catch(console.error);
|
||||
}
|
||||
|
||||
private hideLightbox() {
|
||||
this.Zoom = 1;
|
||||
this.controllersVisible = false;
|
||||
this.status = LightboxStates.Closing;
|
||||
this.fullScreenService.exitFullScreen();
|
||||
this.pause();
|
||||
|
||||
this.animating = true;
|
||||
const lightboxDimension = this.activePhoto.getDimension();
|
||||
lightboxDimension.top -= PageHelper.ScrollY;
|
||||
this.blackCanvasOpacity = 0;
|
||||
|
||||
this.animatePhoto(this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media), this.activePhoto.getDimension());
|
||||
this.animateLightbox(<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth(),
|
||||
height: this.getPhotoFrameHeight()
|
||||
}, lightboxDimension).onDone(() => {
|
||||
this.status = LightboxStates.Closed;
|
||||
this.activePhoto = null;
|
||||
this.activePhotoId = null;
|
||||
this.overlayService.hideOverlay();
|
||||
});
|
||||
|
||||
|
||||
this.hideInfoPanel(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
animatePhoto(from: Dimension, to: Dimension = from): AnimationPlayer {
|
||||
const elem = this._builder.build([
|
||||
style(Dimension.toString(from)),
|
||||
@ -492,14 +229,15 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
])
|
||||
.create(this.mediaElement.elementRef.nativeElement);
|
||||
elem.play();
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
animateLightbox(from: Dimension = <Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth(),
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: this.photoFrameDim.width,
|
||||
height: this.photoFrameDim.height
|
||||
}, to: Dimension = from): AnimationPlayer {
|
||||
const elem = this._builder.build([
|
||||
style(Dimension.toString(from)),
|
||||
@ -511,10 +249,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
||||
public toggleInfoPanel() {
|
||||
|
||||
|
||||
if (this.infoPanelWidth !== 400) {
|
||||
this.showInfoPanel();
|
||||
} else {
|
||||
@ -527,11 +263,12 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.iPvisibilityTimer = window.setTimeout(() => {
|
||||
this.iPvisibilityTimer = null;
|
||||
this.infoPanelVisible = false;
|
||||
this.checkZoomAndDrag();
|
||||
// this.controls.onResize();
|
||||
}, 1000);
|
||||
|
||||
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||
this.infoPanelWidth = 0;
|
||||
this.updatePhotoFrameDim();
|
||||
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||
if (_animate) {
|
||||
this.animatePhoto(starPhotoPos, endPhotoPos);
|
||||
@ -540,37 +277,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.animateLightbox(<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: Math.max(this.getPhotoFrameWidth() - 400, 0),
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: Math.max(this.photoFrameDim.width - 400, 0),
|
||||
height: this.photoFrameDim.height
|
||||
},
|
||||
<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth(),
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: this.photoFrameDim.width,
|
||||
height: this.photoFrameDim.height
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public play() {
|
||||
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.gridPhoto.isVideo() &&
|
||||
!this.mediaElement.Paused) {
|
||||
return;
|
||||
}
|
||||
if (this.navigation.hasNext) {
|
||||
this.nextImage();
|
||||
} else {
|
||||
this.navigateToPhoto(0);
|
||||
}
|
||||
});
|
||||
this.playBackState = PlayBackStates.Play;
|
||||
}
|
||||
|
||||
isInfoPanelAnimating(): boolean {
|
||||
return this.iPvisibilityTimer != null;
|
||||
@ -581,57 +299,85 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
|
||||
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||
this.infoPanelWidth = 400;
|
||||
this.updatePhotoFrameDim();
|
||||
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||
this.animatePhoto(starPhotoPos, endPhotoPos);
|
||||
this.animateLightbox(<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth() + 400,
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: this.photoFrameDim.width + 400,
|
||||
height: this.photoFrameDim.height
|
||||
},
|
||||
<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getPhotoFrameWidth(),
|
||||
height: this.getPhotoFrameHeight()
|
||||
width: this.photoFrameDim.width,
|
||||
height: this.photoFrameDim.height
|
||||
});
|
||||
if (this.iPvisibilityTimer != null) {
|
||||
clearTimeout(this.iPvisibilityTimer);
|
||||
}
|
||||
|
||||
this.checkZoomAndDrag();
|
||||
if (this.controls) {
|
||||
this.controls.resetZoom();
|
||||
}
|
||||
}
|
||||
|
||||
public fastForward() {
|
||||
this.pause();
|
||||
this.timerSub = this.timer.subscribe(() => {
|
||||
if (this.mediaElement.imageLoadFinished === false) {
|
||||
return;
|
||||
}
|
||||
if (this.activePhoto && this.activePhoto.gridPhoto.isVideo() &&
|
||||
!this.mediaElement.Paused) {
|
||||
return;
|
||||
}
|
||||
if (this.navigation.hasNext) {
|
||||
this.nextImage();
|
||||
} else {
|
||||
this.navigateToPhoto(0);
|
||||
}
|
||||
public isVisible(): boolean {
|
||||
return this.status !== LightboxStates.Closed;
|
||||
}
|
||||
|
||||
|
||||
private updatePhotoFrameDim = () => {
|
||||
this.photoFrameDim.width = Math.max(window.innerWidth - this.infoPanelWidth, 0);
|
||||
this.photoFrameDim.height = window.innerHeight;
|
||||
this.photoFrameDim.aspect = Math.round(this.photoFrameDim.width / this.photoFrameDim.height * 100) / 100;
|
||||
};
|
||||
|
||||
private navigateToPhoto(photoIndex: number) {
|
||||
this.router.navigate([],
|
||||
{queryParams: this.queryService.getParams(this.gridPhotoQL.toArray()[photoIndex].gridPhoto.media)}).catch(console.error);
|
||||
}
|
||||
|
||||
private showPhoto(photoIndex: number, resize: boolean = true) {
|
||||
this.activePhoto = null;
|
||||
this.changeDetector.detectChanges();
|
||||
this.updateActivePhoto(photoIndex, resize);
|
||||
}
|
||||
|
||||
private hideLightbox() {
|
||||
if (this.controls) {
|
||||
this.controls.resetZoom();
|
||||
}
|
||||
this.status = LightboxStates.Closing;
|
||||
this.fullScreenService.exitFullScreen();
|
||||
|
||||
if (this.controls) {
|
||||
this.controls.pause();
|
||||
}
|
||||
|
||||
|
||||
this.animating = true;
|
||||
const lightboxDimension = this.activePhoto.getDimension();
|
||||
lightboxDimension.top -= PageHelper.ScrollY;
|
||||
this.blackCanvasOpacity = 0;
|
||||
|
||||
this.animatePhoto(this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media), this.activePhoto.getDimension());
|
||||
this.animateLightbox(<Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.photoFrameDim.width,
|
||||
height: this.photoFrameDim.height
|
||||
}, lightboxDimension).onDone(() => {
|
||||
this.status = LightboxStates.Closed;
|
||||
this.activePhoto = null;
|
||||
this.activePhotoId = null;
|
||||
this.overlayService.hideOverlay();
|
||||
});
|
||||
this.playBackState = PlayBackStates.FastForward;
|
||||
}
|
||||
|
||||
|
||||
public getPhotoFrameWidth(): number {
|
||||
return Math.max(window.innerWidth - this.infoPanelWidth, 0);
|
||||
}
|
||||
this.hideInfoPanel(false);
|
||||
|
||||
public getPhotoFrameHeight(): number {
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
public getWindowAspectRatio(): number {
|
||||
return Math.round(this.getPhotoFrameWidth() / this.getPhotoFrameHeight() * 100) / 100;
|
||||
}
|
||||
|
||||
private updateActivePhoto(photoIndex: number, resize: boolean = true) {
|
||||
@ -653,38 +399,13 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
const to = this.activePhoto.getDimension();
|
||||
|
||||
// if target image out of screen -> scroll to there
|
||||
if (PageHelper.ScrollY > to.top || PageHelper.ScrollY + this.getPhotoFrameHeight() < to.top) {
|
||||
if (PageHelper.ScrollY > to.top || PageHelper.ScrollY + this.photoFrameDim.height < to.top) {
|
||||
PageHelper.ScrollY = to.top;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@HostListener('mousemove')
|
||||
onMouseMove() {
|
||||
this.showControls();
|
||||
}
|
||||
|
||||
private showControls() {
|
||||
this.controllersDimmed = false;
|
||||
if (this.visibilityTimer != null) {
|
||||
clearTimeout(this.visibilityTimer);
|
||||
}
|
||||
this.visibilityTimer = window.setTimeout(this.hideControls, 2000);
|
||||
}
|
||||
|
||||
private hideControls = () => {
|
||||
this.controllersDimmed = true;
|
||||
};
|
||||
|
||||
|
||||
public pause() {
|
||||
if (this.timerSub != null) {
|
||||
this.timerSub.unsubscribe();
|
||||
}
|
||||
this.playBackState = PlayBackStates.Paused;
|
||||
}
|
||||
|
||||
private findPhotoComponent(media: MediaDTO): GalleryPhotoComponent {
|
||||
const galleryPhotoComponents = this.gridPhotoQL.toArray();
|
||||
for (let i = 0; i < galleryPhotoComponents.length; i++) {
|
||||
@ -699,30 +420,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
const photoAspect = photo.metadata.size.width / photo.metadata.size.height;
|
||||
const windowAspect = this.getPhotoFrameWidth() / this.getPhotoFrameHeight();
|
||||
const windowAspect = this.photoFrameDim.aspect;
|
||||
if (photoAspect < windowAspect) {
|
||||
width = Math.round(photo.metadata.size.width * (this.getPhotoFrameHeight() / photo.metadata.size.height));
|
||||
height = this.getPhotoFrameHeight();
|
||||
width = Math.round(photo.metadata.size.width * (this.photoFrameDim.height / photo.metadata.size.height));
|
||||
height = this.photoFrameDim.height;
|
||||
} else {
|
||||
width = this.getPhotoFrameWidth();
|
||||
height = Math.round(photo.metadata.size.height * (this.getPhotoFrameWidth() / photo.metadata.size.width));
|
||||
width = this.photoFrameDim.width;
|
||||
height = Math.round(photo.metadata.size.height * (this.photoFrameDim.width / photo.metadata.size.width));
|
||||
}
|
||||
const top = (this.getPhotoFrameHeight() / 2 - height / 2);
|
||||
const left = (this.getPhotoFrameWidth() / 2 - width / 2);
|
||||
const top = (this.photoFrameDim.height / 2 - height / 2);
|
||||
const left = (this.photoFrameDim.width / 2 - width / 2);
|
||||
|
||||
return <Dimension>{top: top, left: left, width: width, height: height};
|
||||
}
|
||||
|
||||
public isVisible(): boolean {
|
||||
return this.status !== LightboxStates.Closed;
|
||||
}
|
||||
|
||||
|
||||
get Title(): string {
|
||||
if (!this.activePhoto) {
|
||||
return null;
|
||||
}
|
||||
return (<PhotoDTO>this.activePhoto.gridPhoto.media).metadata.caption;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user