mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
implementing lightbox image zoom
This commit is contained in:
parent
ada2c007df
commit
531759f54b
@ -80,11 +80,16 @@ import {FileSizePipe} from './pipes/FileSizePipe';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyHammerConfig extends HammerGestureConfig {
|
export class MyHammerConfig extends HammerGestureConfig {
|
||||||
overrides = <any>{
|
events: string[] = ['pinch'];
|
||||||
'swipe': {direction: 31} // enable swipe up
|
overrides = {
|
||||||
|
pan: {threshold: 1},
|
||||||
|
swipe: {direction: 31}, // enable swipe up
|
||||||
|
pinch: {enable: true}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class CustomUrlSerializer implements UrlSerializer {
|
export class CustomUrlSerializer implements UrlSerializer {
|
||||||
private _defaultUrlSerializer: DefaultUrlSerializer = new DefaultUrlSerializer();
|
private _defaultUrlSerializer: DefaultUrlSerializer = new DefaultUrlSerializer();
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import {Config} from '../../../common/config/public/Config';
|
|||||||
import {ShareService} from './share.service';
|
import {ShareService} from './share.service';
|
||||||
import {NavigationService} from '../model/navigation.service';
|
import {NavigationService} from '../model/navigation.service';
|
||||||
import {SortingMethods} from '../../../common/entities/SortingMethods';
|
import {SortingMethods} from '../../../common/entities/SortingMethods';
|
||||||
import {QueryService} from '../model/query.service';
|
|
||||||
import {QueryParams} from '../../../common/QueryParams';
|
import {QueryParams} from '../../../common/QueryParams';
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
: {{VideoData.duration | duration}}</div>
|
: {{VideoData.duration | duration}}</div>
|
||||||
<div class="col-6" *ngIf="VideoData.bitRate">
|
<div class="col-6" *ngIf="VideoData.bitRate">
|
||||||
<ng-container i18n>bit rate</ng-container>
|
<ng-container i18n>bit rate</ng-container>
|
||||||
: {{calcSize(VideoData.bitRate)}}/s
|
: {{VideoData.bitRate | fileSize}}/s
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
<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"
|
||||||
|
[drag]="drag"
|
||||||
[windowAspect]="getWindowAspectRatio()"
|
[windowAspect]="getWindowAspectRatio()"
|
||||||
#photo>
|
#photo>
|
||||||
</app-gallery-lightbox-media>
|
</app-gallery-lightbox-media>
|
||||||
@ -58,18 +60,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="swipeable-container"
|
<div id="swipeable-container"
|
||||||
(swipeleft)="nextImage()"
|
(swipeleft)="zoom == 1 && nextImage()"
|
||||||
(swiperight)="prevImage()"
|
(swiperight)="zoom == 1 && prevImage()"
|
||||||
(swipeup)="hide()"
|
(swipeup)="zoom == 1 && hide()"
|
||||||
(click)="photo.playPause()">
|
(click)="photo.playPause()">
|
||||||
|
|
||||||
|
|
||||||
<div class="navigation-arrow highlight"
|
<div class="navigation-arrow highlight"
|
||||||
*ngIf="navigation.hasPrev" title="key: left arrow" id="leftArrow" i18n-title
|
*ngIf="navigation.hasPrev && zoom == 1" title="key: left arrow" id="leftArrow" i18n-title
|
||||||
(click)="prevImage()"><span
|
(click)="prevImage()"><span
|
||||||
class="oi oi-chevron-left"></span></div>
|
class="oi oi-chevron-left"></span></div>
|
||||||
<div class="navigation-arrow highlight"
|
<div class="navigation-arrow highlight"
|
||||||
*ngIf="navigation.hasNext" title="key: right arrow" id="rightArrow" i18n-title
|
*ngIf="navigation.hasNext && zoom == 1" title="key: right arrow" id="rightArrow" i18n-title
|
||||||
(click)="nextImage()"><span
|
(click)="nextImage()"><span
|
||||||
class="oi oi-chevron-right"></span></div>
|
class="oi oi-chevron-right"></span></div>
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ 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 {$e} from '@angular/compiler/src/chars';
|
||||||
|
import {Utils} from '../../../../common/Utils';
|
||||||
|
|
||||||
export enum LightboxStates {
|
export enum LightboxStates {
|
||||||
Open = 1,
|
Open = 1,
|
||||||
@ -70,7 +72,10 @@ 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 drag = {x: 0, y: 0};
|
||||||
|
private prevDrag = {x: 0, y: 0};
|
||||||
|
private prevZoom = 1;
|
||||||
|
|
||||||
constructor(public fullScreenService: FullScreenService,
|
constructor(public fullScreenService: FullScreenService,
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
@ -119,6 +124,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
if (this.activePhoto && this.queryService.getMediaStringId(this.activePhoto.gridPhoto.media) === photoStringId) {
|
if (this.activePhoto && this.queryService.getMediaStringId(this.activePhoto.gridPhoto.media) === photoStringId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setZoom(1);
|
||||||
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;
|
||||||
@ -150,6 +157,128 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('pan', ['$event'])
|
||||||
|
dragging($event: any) {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:wheel', ['$event'])
|
||||||
|
onWheel($event: any) {
|
||||||
|
this.setZoom(this.zoom + ($event.deltaY < 0 ? this.zoom / 10 : -this.zoom / 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('pinch', ['$event'])
|
||||||
|
pinch($event: any) {
|
||||||
|
this.setZoom(this.prevZoom * $event.scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('pinchend', ['$event'])
|
||||||
|
pinchend($event: any) {
|
||||||
|
this.setZoom(this.prevZoom * $event.scale);
|
||||||
|
this.prevZoom = this.zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('tap', ['$event'])
|
||||||
|
tao($event: any) {
|
||||||
|
if ($event.tapCount < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.zoom > 1) {
|
||||||
|
this.setZoom(1);
|
||||||
|
this.prevZoom = this.zoom;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.setZoom(5);
|
||||||
|
this.prevZoom = this.zoom;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private setZoom(zoom: number) {
|
||||||
|
if (!this.activePhoto || this.activePhoto.gridPhoto.isVideo()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zoom < 1) {
|
||||||
|
zoom = 1;
|
||||||
|
}
|
||||||
|
if (zoom > 10) {
|
||||||
|
zoom = 10;
|
||||||
|
}
|
||||||
|
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 > 10) {
|
||||||
|
this.zoom = 10;
|
||||||
|
}
|
||||||
|
fixDrag(this.drag);
|
||||||
|
fixDrag(this.prevDrag);
|
||||||
|
}
|
||||||
|
|
||||||
//noinspection JSUnusedGlobalSymbols
|
//noinspection JSUnusedGlobalSymbols
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize() {
|
onResize() {
|
||||||
@ -185,6 +314,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public showLigthbox(photo: MediaDTO) {
|
public showLigthbox(photo: MediaDTO) {
|
||||||
|
this.setZoom(1);
|
||||||
this.controllersVisible = true;
|
this.controllersVisible = true;
|
||||||
this.showControls();
|
this.showControls();
|
||||||
this.status = LightboxStates.Open;
|
this.status = LightboxStates.Open;
|
||||||
@ -275,6 +405,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private hideLigthbox() {
|
private hideLigthbox() {
|
||||||
|
this.setZoom(1);
|
||||||
this.controllersVisible = false;
|
this.controllersVisible = false;
|
||||||
this.status = LightboxStates.Closing;
|
this.status = LightboxStates.Closing;
|
||||||
this.fullScreenService.exitFullScreen();
|
this.fullScreenService.exitFullScreen();
|
||||||
@ -347,6 +478,7 @@ 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();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media);
|
||||||
@ -417,6 +549,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
|||||||
if (this.iPvisibilityTimer != null) {
|
if (this.iPvisibilityTimer != null) {
|
||||||
clearTimeout(this.iPvisibilityTimer);
|
clearTimeout(this.iPvisibilityTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkZoomAndDrag();
|
||||||
}
|
}
|
||||||
|
|
||||||
public fastForward() {
|
public fastForward() {
|
||||||
|
@ -2,23 +2,26 @@
|
|||||||
<img *ngIf="showThumbnail()"
|
<img *ngIf="showThumbnail()"
|
||||||
[style.width.%]="imageSize.width"
|
[style.width.%]="imageSize.width"
|
||||||
[style.height.%]="imageSize.height"
|
[style.height.%]="imageSize.height"
|
||||||
|
[style.transform]="ImageTransform"
|
||||||
[src]="thumbnailSrc"/>
|
[src]="thumbnailSrc"/>
|
||||||
|
|
||||||
<img *ngIf="gridMedia !== null && gridMedia.isPhoto() && loadMedia && photoSrc"
|
<img *ngIf="gridMedia !== null && gridMedia.isPhoto() && loadMedia && photoSrc"
|
||||||
[style.width.%]="imageSize.width"
|
[style.width.%]="imageSize.width"
|
||||||
[style.height.%]="imageSize.height"
|
[style.height.%]="imageSize.height"
|
||||||
|
[style.transform]="ImageTransform"
|
||||||
[src]="photoSrc"
|
[src]="photoSrc"
|
||||||
|
[alt]="gridMedia.media.name"
|
||||||
(load)="onImageLoad()"
|
(load)="onImageLoad()"
|
||||||
(error)="onImageError()"/>
|
(error)="onImageError()"/>
|
||||||
|
|
||||||
<video #video
|
<video #video
|
||||||
*ngIf="gridMedia !== null && gridMedia.isVideo() && loadMedia"
|
*ngIf="gridMedia !== null && gridMedia.isVideo() && loadMedia"
|
||||||
[style.width.%]="imageSize.width"
|
[style.width.%]="imageSize.width"
|
||||||
[style.height.%]="imageSize.height"
|
[style.height.%]="imageSize.height"
|
||||||
(loadstart)="onImageLoad()"
|
(loadstart)="onImageLoad()"
|
||||||
autoplay
|
autoplay
|
||||||
(error)="onImageError()"
|
(error)="onImageError()"
|
||||||
(timeupdate)="onVideoProgress()" >
|
(timeupdate)="onVideoProgress()">
|
||||||
<source [src]="gridMedia.getPhotoPath()" type="{{getVideoType()}}">
|
<source [src]="gridMedia.getPhotoPath()" type="{{getVideoType()}}">
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import {Component, ElementRef, Input, Output, OnChanges, ViewChild} from '@angul
|
|||||||
import {GridMedia} from '../../grid/GridMedia';
|
import {GridMedia} from '../../grid/GridMedia';
|
||||||
import {FixOrientationPipe} from '../../FixOrientationPipe';
|
import {FixOrientationPipe} from '../../FixOrientationPipe';
|
||||||
import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
||||||
|
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-lightbox-media',
|
selector: 'app-gallery-lightbox-media',
|
||||||
@ -13,6 +14,8 @@ export class GalleryLightboxMediaComponent implements OnChanges {
|
|||||||
@Input() gridMedia: GridMedia;
|
@Input() gridMedia: GridMedia;
|
||||||
@Input() loadMedia = false;
|
@Input() loadMedia = false;
|
||||||
@Input() windowAspect = 1;
|
@Input() windowAspect = 1;
|
||||||
|
@Input() zoom = 1;
|
||||||
|
@Input() drag = {x: 0, y: 0};
|
||||||
|
|
||||||
@ViewChild('video') video: ElementRef<HTMLVideoElement>;
|
@ViewChild('video') video: ElementRef<HTMLVideoElement>;
|
||||||
|
|
||||||
@ -27,7 +30,14 @@ export class GalleryLightboxMediaComponent implements OnChanges {
|
|||||||
photoSrc: string = null;
|
photoSrc: string = null;
|
||||||
private videoProgress = 0;
|
private videoProgress = 0;
|
||||||
|
|
||||||
constructor(public elementRef: ElementRef) {
|
constructor(public elementRef: ElementRef,
|
||||||
|
private _sanitizer: DomSanitizer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get ImageTransform(): SafeStyle {
|
||||||
|
return this._sanitizer.bypassSecurityTrustStyle('scale(' + this.zoom +
|
||||||
|
') translate(calc(' + -50 / this.zoom + '% + ' + this.drag.x / this.zoom + 'px), calc(' +
|
||||||
|
-50 / this.zoom + '% + ' + this.drag.y / this.zoom + 'px))');
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user