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;
|
this.samplePhotos[name] = media;
|
||||||
}
|
}
|
||||||
return this.samplePhotos[name];
|
|
||||||
|
|
||||||
|
return this.samplePhotos[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadAll(): Promise<void> {
|
async loadAll(): Promise<void> {
|
||||||
|
@ -79,6 +79,7 @@ import {FacesService} from './ui/faces/faces.service';
|
|||||||
import {FaceComponent} from './ui/faces/face/face.component';
|
import {FaceComponent} from './ui/faces/face/face.component';
|
||||||
import {VersionService} from './model/version.service';
|
import {VersionService} from './model/version.service';
|
||||||
import { DirectoriesComponent } from './ui/gallery/directories/directories.component';
|
import { DirectoriesComponent } from './ui/gallery/directories/directories.component';
|
||||||
|
import {ControlsLightboxComponent} from './ui/gallery/lightbox/controls/controls.lightbox.gallery.component';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -162,6 +163,7 @@ export function translationsFactory(locale: string) {
|
|||||||
GalleryPhotoComponent,
|
GalleryPhotoComponent,
|
||||||
AdminComponent,
|
AdminComponent,
|
||||||
InfoPanelLightboxComponent,
|
InfoPanelLightboxComponent,
|
||||||
|
ControlsLightboxComponent,
|
||||||
RandomQueryBuilderGalleryComponent,
|
RandomQueryBuilderGalleryComponent,
|
||||||
// Face
|
// Face
|
||||||
FaceComponent,
|
FaceComponent,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<div body class="container">
|
<div body class="container">
|
||||||
<ng-template [ngIf]="renderedDirGroups">
|
<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).
|
Listing <strong>{{duplicateCount.pairs}}</strong> duplicates ({{duplicateCount.photos}} photos).
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-secondary" role="alert" *ngIf=" duplicateCount.photos ==0" i18n>
|
<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>
|
<div class="lightbox" #lightbox>
|
||||||
<app-gallery-lightbox-media [gridMedia]="activePhoto ? activePhoto.gridPhoto : null"
|
<app-gallery-lightbox-media [gridMedia]="activePhoto ? activePhoto.gridPhoto : null"
|
||||||
[loadMedia]="!animating"
|
[loadMedia]="!animating"
|
||||||
[zoom]="zoom"
|
[zoom]="controls ? controls.Zoom : 1"
|
||||||
[drag]="drag"
|
[drag]="controls ? controls.drag : {x:0,y:0}"
|
||||||
[windowAspect]="getWindowAspectRatio()"
|
[windowAspect]="photoFrameDim.aspect"
|
||||||
#photo>
|
#photo>
|
||||||
</app-gallery-lightbox-media>
|
</app-gallery-lightbox-media>
|
||||||
</div>
|
<app-lightbox-controls
|
||||||
<div
|
|
||||||
*ngIf="controllersVisible"
|
|
||||||
id="controllers-container"
|
|
||||||
#controls
|
#controls
|
||||||
[style.width.px]="getPhotoFrameWidth()"
|
[activePhoto]="activePhoto"
|
||||||
[ngClass]="(controllersDimmed && !controllersAlwaysOn) ? (activePhoto && activePhoto.gridPhoto.isVideo() ? 'dim-controls-video' :'dim-controls'): ''">
|
(closed)="hide()"
|
||||||
<div class="controls-caption" *ngIf="Title">{{Title}}</div>
|
[navigation]="navigation"
|
||||||
<div class="controls controls-top">
|
(nextPhoto)="nextImage()"
|
||||||
<a *ngIf="activePhoto"
|
(previousPhoto)="prevImage()"
|
||||||
class="highlight control-button"
|
(toggleInfoPanel)="toggleInfoPanel()"
|
||||||
[href]="activePhoto.gridPhoto.getPhotoPath()"
|
(toggleFullScreen)="toggleFullscreen()"
|
||||||
[download]="activePhoto.gridPhoto.media.name">
|
[photoFrameDim]="photoFrameDim"
|
||||||
<span class="oi oi-data-transfer-download"
|
[mediaElement]="mediaElement">
|
||||||
title="download" i18n-title></span>
|
</app-lightbox-controls>
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class=" highlight control-button" (click)="toggleInfoPanel()"
|
|
||||||
title="info key: i" i18n-title>
|
|
||||||
<span class="oi oi-info"></span>
|
|
||||||
</div>
|
</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"
|
<app-info-panel *ngIf="activePhoto && infoPanelVisible"
|
||||||
id="info-panel"
|
id="info-panel"
|
||||||
[style.width.px]="infoPanelWidth"
|
[style.width.px]="infoPanelWidth"
|
||||||
|
@ -1,21 +1,11 @@
|
|||||||
import {
|
import {ChangeDetectorRef, Component, ElementRef, HostListener, OnDestroy, OnInit, QueryList, ViewChild} from '@angular/core';
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
ElementRef,
|
|
||||||
HostListener,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
QueryList,
|
|
||||||
ViewChild
|
|
||||||
} from '@angular/core';
|
|
||||||
import {GalleryPhotoComponent} from '../grid/photo/photo.grid.gallery.component';
|
import {GalleryPhotoComponent} from '../grid/photo/photo.grid.gallery.component';
|
||||||
import {Dimension} from '../../../model/IRenderable';
|
import {Dimension} from '../../../model/IRenderable';
|
||||||
import {FullScreenService} from '../fullscreen.service';
|
import {FullScreenService} from '../fullscreen.service';
|
||||||
import {OverlayService} from '../overlay.service';
|
import {OverlayService} from '../overlay.service';
|
||||||
import {animate, AnimationBuilder, AnimationPlayer, style} from '@angular/animations';
|
import {animate, AnimationBuilder, AnimationPlayer, style} from '@angular/animations';
|
||||||
import {GalleryLightboxMediaComponent} from './media/media.lightbox.gallery.component';
|
import {GalleryLightboxMediaComponent} from './media/media.lightbox.gallery.component';
|
||||||
import {Observable, Subscription, timer} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
import {filter} from 'rxjs/operators';
|
|
||||||
import {ActivatedRoute, Params, Router} from '@angular/router';
|
import {ActivatedRoute, Params, Router} from '@angular/router';
|
||||||
import {PageHelper} from '../../../model/page.helper';
|
import {PageHelper} from '../../../model/page.helper';
|
||||||
import {QueryService} from '../../../model/query.service';
|
import {QueryService} from '../../../model/query.service';
|
||||||
@ -23,6 +13,7 @@ import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
|||||||
import {QueryParams} from '../../../../../common/QueryParams';
|
import {QueryParams} from '../../../../../common/QueryParams';
|
||||||
import {GalleryService} from '../gallery.service';
|
import {GalleryService} from '../gallery.service';
|
||||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||||
|
import {ControlsLightboxComponent} from './controls/controls.lightbox.gallery.component';
|
||||||
|
|
||||||
export enum LightboxStates {
|
export enum LightboxStates {
|
||||||
Open = 1,
|
Open = 1,
|
||||||
@ -30,48 +21,22 @@ export enum LightboxStates {
|
|||||||
Closed = 3
|
Closed = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PlayBackStates {
|
|
||||||
Paused = 1,
|
|
||||||
Play = 2,
|
|
||||||
FastForward = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-lightbox',
|
selector: 'app-gallery-lightbox',
|
||||||
styleUrls: ['./lightbox.gallery.component.css', './inputrange.css'],
|
styleUrls: ['./lightbox.gallery.component.css'],
|
||||||
templateUrl: './lightbox.gallery.component.html'
|
templateUrl: './lightbox.gallery.component.html'
|
||||||
})
|
})
|
||||||
export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
readonly MAX_ZOOM = 10;
|
|
||||||
|
|
||||||
@ViewChild('photo') mediaElement: GalleryLightboxMediaComponent;
|
@ViewChild('photo') mediaElement: GalleryLightboxMediaComponent;
|
||||||
|
@ViewChild('controls') controls: ControlsLightboxComponent;
|
||||||
@ViewChild('lightbox') lightboxElement: ElementRef;
|
@ViewChild('lightbox') lightboxElement: ElementRef;
|
||||||
@ViewChild('root') root: ElementRef;
|
@ViewChild('root') root: ElementRef;
|
||||||
|
|
||||||
public navigation = {hasPrev: true, hasNext: true};
|
public navigation = {hasPrev: true, hasNext: true};
|
||||||
public blackCanvasOpacity = 0;
|
public blackCanvasOpacity = 0;
|
||||||
|
|
||||||
private activePhotoId: number = null;
|
|
||||||
public activePhoto: GalleryPhotoComponent;
|
public activePhoto: GalleryPhotoComponent;
|
||||||
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
|
||||||
|
|
||||||
public status: LightboxStates = LightboxStates.Closed;
|
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 infoPanelVisible = false;
|
||||||
public infoPanelWidth = 0;
|
public infoPanelWidth = 0;
|
||||||
public animating = false;
|
public animating = false;
|
||||||
@ -79,10 +44,17 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
iPvisibilityTimer: number = null;
|
iPvisibilityTimer: number = null;
|
||||||
visibilityTimer: number = null;
|
visibilityTimer: number = null;
|
||||||
delayedMediaShow: string = null;
|
delayedMediaShow: string = null;
|
||||||
public zoom = 1;
|
public photoFrameDim = {width: 1, height: 1, aspect: 1};
|
||||||
public drag = {x: 0, y: 0};
|
private activePhotoId: number = null;
|
||||||
private prevDrag = {x: 0, y: 0};
|
private gridPhotoQL: QueryList<GalleryPhotoComponent>;
|
||||||
private prevZoom = 1;
|
private subscription: {
|
||||||
|
photosChange: Subscription,
|
||||||
|
route: Subscription
|
||||||
|
} = {
|
||||||
|
photosChange: null,
|
||||||
|
route: null
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
constructor(public fullScreenService: FullScreenService,
|
constructor(public fullScreenService: FullScreenService,
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -94,8 +66,24 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
private route: ActivatedRoute) {
|
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 {
|
ngOnInit(): void {
|
||||||
this.timer = timer(1000, 2000);
|
this.updatePhotoFrameDim();
|
||||||
this.subscription.route = this.route.queryParams.subscribe((params: Params) => {
|
this.subscription.route = this.route.queryParams.subscribe((params: Params) => {
|
||||||
if (params[QueryParams.gallery.photo] && params[QueryParams.gallery.photo] !== '') {
|
if (params[QueryParams.gallery.photo] && params[QueryParams.gallery.photo] !== '') {
|
||||||
if (!this.gridPhotoQL) {
|
if (!this.gridPhotoQL) {
|
||||||
@ -109,9 +97,10 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.pause();
|
if (this.controls) {
|
||||||
|
this.controls.pause();
|
||||||
|
}
|
||||||
if (this.subscription.photosChange != null) {
|
if (this.subscription.photosChange != null) {
|
||||||
this.subscription.photosChange.unsubscribe();
|
this.subscription.photosChange.unsubscribe();
|
||||||
}
|
}
|
||||||
@ -132,7 +121,9 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Zoom = 1;
|
if (this.controls) {
|
||||||
|
this.controls.resetZoom();
|
||||||
|
}
|
||||||
const photo = this.gridPhotoQL.find(i => this.queryService.getMediaStringId(i.gridPhoto.media) === photoStringId);
|
const photo = this.gridPhotoQL.find(i => this.queryService.getMediaStringId(i.gridPhoto.media) === photoStringId);
|
||||||
if (!photo) {
|
if (!photo) {
|
||||||
return this.delayedMediaShow = photoStringId;
|
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
|
//noinspection JSUnusedGlobalSymbols
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize() {
|
onResize() {
|
||||||
@ -330,37 +162,30 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
this.animateLightbox();
|
this.animateLightbox();
|
||||||
this.updateActivePhoto(this.activePhotoId);
|
this.updateActivePhoto(this.activePhotoId);
|
||||||
}
|
}
|
||||||
|
this.updatePhotoFrameDim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextImage() {
|
public nextImage() {
|
||||||
if (this.activePhotoId + 1 < this.gridPhotoQL.length) {
|
if (this.activePhotoId + 1 < this.gridPhotoQL.length) {
|
||||||
this.navigateToPhoto(this.activePhotoId + 1);
|
this.navigateToPhoto(this.activePhotoId + 1);
|
||||||
|
} else {
|
||||||
|
this.navigateToPhoto(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public prevImage() {
|
public prevImage() {
|
||||||
this.pause();
|
if (this.controls) {
|
||||||
|
this.controls.pause();
|
||||||
|
}
|
||||||
if (this.activePhotoId > 0) {
|
if (this.activePhotoId > 0) {
|
||||||
this.navigateToPhoto(this.activePhotoId - 1);
|
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) {
|
public showLigthbox(photo: MediaDTO) {
|
||||||
this.Zoom = 1;
|
if (this.controls) {
|
||||||
this.controllersVisible = true;
|
this.controls.resetZoom();
|
||||||
this.showControls();
|
}
|
||||||
this.status = LightboxStates.Open;
|
this.status = LightboxStates.Open;
|
||||||
const selectedPhoto = this.findPhotoComponent(photo);
|
const selectedPhoto = this.findPhotoComponent(photo);
|
||||||
if (selectedPhoto === null) {
|
if (selectedPhoto === null) {
|
||||||
@ -378,8 +203,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
<Dimension>{
|
<Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: this.getPhotoFrameWidth(),
|
width: this.photoFrameDim.width,
|
||||||
height: this.getPhotoFrameHeight()
|
height: this.photoFrameDim.height
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -391,99 +216,11 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
this.showPhoto(this.gridPhotoQL.toArray().indexOf(selectedPhoto), false);
|
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() {
|
public hide() {
|
||||||
this.router.navigate([],
|
this.router.navigate([],
|
||||||
{queryParams: this.queryService.getParams()}).catch(console.error);
|
{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 {
|
animatePhoto(from: Dimension, to: Dimension = from): AnimationPlayer {
|
||||||
const elem = this._builder.build([
|
const elem = this._builder.build([
|
||||||
style(Dimension.toString(from)),
|
style(Dimension.toString(from)),
|
||||||
@ -492,14 +229,15 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
])
|
])
|
||||||
.create(this.mediaElement.elementRef.nativeElement);
|
.create(this.mediaElement.elementRef.nativeElement);
|
||||||
elem.play();
|
elem.play();
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
animateLightbox(from: Dimension = <Dimension>{
|
animateLightbox(from: Dimension = <Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: this.getPhotoFrameWidth(),
|
width: this.photoFrameDim.width,
|
||||||
height: this.getPhotoFrameHeight()
|
height: this.photoFrameDim.height
|
||||||
}, to: Dimension = from): AnimationPlayer {
|
}, to: Dimension = from): AnimationPlayer {
|
||||||
const elem = this._builder.build([
|
const elem = this._builder.build([
|
||||||
style(Dimension.toString(from)),
|
style(Dimension.toString(from)),
|
||||||
@ -511,10 +249,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public toggleInfoPanel() {
|
public toggleInfoPanel() {
|
||||||
|
|
||||||
|
|
||||||
if (this.infoPanelWidth !== 400) {
|
if (this.infoPanelWidth !== 400) {
|
||||||
this.showInfoPanel();
|
this.showInfoPanel();
|
||||||
} else {
|
} else {
|
||||||
@ -527,11 +263,12 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
this.iPvisibilityTimer = window.setTimeout(() => {
|
this.iPvisibilityTimer = window.setTimeout(() => {
|
||||||
this.iPvisibilityTimer = null;
|
this.iPvisibilityTimer = null;
|
||||||
this.infoPanelVisible = false;
|
this.infoPanelVisible = false;
|
||||||
this.checkZoomAndDrag();
|
// this.controls.onResize();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||||
this.infoPanelWidth = 0;
|
this.infoPanelWidth = 0;
|
||||||
|
this.updatePhotoFrameDim();
|
||||||
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||||
if (_animate) {
|
if (_animate) {
|
||||||
this.animatePhoto(starPhotoPos, endPhotoPos);
|
this.animatePhoto(starPhotoPos, endPhotoPos);
|
||||||
@ -540,37 +277,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
this.animateLightbox(<Dimension>{
|
this.animateLightbox(<Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: Math.max(this.getPhotoFrameWidth() - 400, 0),
|
width: Math.max(this.photoFrameDim.width - 400, 0),
|
||||||
height: this.getPhotoFrameHeight()
|
height: this.photoFrameDim.height
|
||||||
},
|
},
|
||||||
<Dimension>{
|
<Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: this.getPhotoFrameWidth(),
|
width: this.photoFrameDim.width,
|
||||||
height: this.getPhotoFrameHeight()
|
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 {
|
isInfoPanelAnimating(): boolean {
|
||||||
return this.iPvisibilityTimer != null;
|
return this.iPvisibilityTimer != null;
|
||||||
@ -581,57 +299,85 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||||
this.infoPanelWidth = 400;
|
this.infoPanelWidth = 400;
|
||||||
|
this.updatePhotoFrameDim();
|
||||||
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
const endPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||||
this.animatePhoto(starPhotoPos, endPhotoPos);
|
this.animatePhoto(starPhotoPos, endPhotoPos);
|
||||||
this.animateLightbox(<Dimension>{
|
this.animateLightbox(<Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: this.getPhotoFrameWidth() + 400,
|
width: this.photoFrameDim.width + 400,
|
||||||
height: this.getPhotoFrameHeight()
|
height: this.photoFrameDim.height
|
||||||
},
|
},
|
||||||
<Dimension>{
|
<Dimension>{
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: this.getPhotoFrameWidth(),
|
width: this.photoFrameDim.width,
|
||||||
height: this.getPhotoFrameHeight()
|
height: this.photoFrameDim.height
|
||||||
});
|
});
|
||||||
if (this.iPvisibilityTimer != null) {
|
if (this.iPvisibilityTimer != null) {
|
||||||
clearTimeout(this.iPvisibilityTimer);
|
clearTimeout(this.iPvisibilityTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkZoomAndDrag();
|
if (this.controls) {
|
||||||
|
this.controls.resetZoom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fastForward() {
|
public isVisible(): boolean {
|
||||||
this.pause();
|
return this.status !== LightboxStates.Closed;
|
||||||
this.timerSub = this.timer.subscribe(() => {
|
|
||||||
if (this.mediaElement.imageLoadFinished === false) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (this.activePhoto && this.activePhoto.gridPhoto.isVideo() &&
|
|
||||||
!this.mediaElement.Paused) {
|
|
||||||
return;
|
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);
|
||||||
}
|
}
|
||||||
if (this.navigation.hasNext) {
|
|
||||||
this.nextImage();
|
private showPhoto(photoIndex: number, resize: boolean = true) {
|
||||||
} else {
|
this.activePhoto = null;
|
||||||
this.navigateToPhoto(0);
|
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 {
|
this.hideInfoPanel(false);
|
||||||
return Math.max(window.innerWidth - this.infoPanelWidth, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
private updateActivePhoto(photoIndex: number, resize: boolean = true) {
|
||||||
@ -653,38 +399,13 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
const to = this.activePhoto.getDimension();
|
const to = this.activePhoto.getDimension();
|
||||||
|
|
||||||
// if target image out of screen -> scroll to there
|
// 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;
|
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 {
|
private findPhotoComponent(media: MediaDTO): GalleryPhotoComponent {
|
||||||
const galleryPhotoComponents = this.gridPhotoQL.toArray();
|
const galleryPhotoComponents = this.gridPhotoQL.toArray();
|
||||||
for (let i = 0; i < galleryPhotoComponents.length; i++) {
|
for (let i = 0; i < galleryPhotoComponents.length; i++) {
|
||||||
@ -699,30 +420,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
let width = 0;
|
let width = 0;
|
||||||
let height = 0;
|
let height = 0;
|
||||||
const photoAspect = photo.metadata.size.width / photo.metadata.size.height;
|
const photoAspect = photo.metadata.size.width / photo.metadata.size.height;
|
||||||
const windowAspect = this.getPhotoFrameWidth() / this.getPhotoFrameHeight();
|
const windowAspect = this.photoFrameDim.aspect;
|
||||||
if (photoAspect < windowAspect) {
|
if (photoAspect < windowAspect) {
|
||||||
width = Math.round(photo.metadata.size.width * (this.getPhotoFrameHeight() / photo.metadata.size.height));
|
width = Math.round(photo.metadata.size.width * (this.photoFrameDim.height / photo.metadata.size.height));
|
||||||
height = this.getPhotoFrameHeight();
|
height = this.photoFrameDim.height;
|
||||||
} else {
|
} else {
|
||||||
width = this.getPhotoFrameWidth();
|
width = this.photoFrameDim.width;
|
||||||
height = Math.round(photo.metadata.size.height * (this.getPhotoFrameWidth() / photo.metadata.size.width));
|
height = Math.round(photo.metadata.size.height * (this.photoFrameDim.width / photo.metadata.size.width));
|
||||||
}
|
}
|
||||||
const top = (this.getPhotoFrameHeight() / 2 - height / 2);
|
const top = (this.photoFrameDim.height / 2 - height / 2);
|
||||||
const left = (this.getPhotoFrameWidth() / 2 - width / 2);
|
const left = (this.photoFrameDim.width / 2 - width / 2);
|
||||||
|
|
||||||
return <Dimension>{top: top, left: left, width: width, height: height};
|
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