diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 6c250371..1aca9696 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -80,11 +80,16 @@ import {FileSizePipe} from './pipes/FileSizePipe'; @Injectable() export class MyHammerConfig extends HammerGestureConfig { - overrides = { - 'swipe': {direction: 31} // enable swipe up + events: string[] = ['pinch']; + overrides = { + pan: {threshold: 1}, + swipe: {direction: 31}, // enable swipe up + pinch: {enable: true} }; } + + export class CustomUrlSerializer implements UrlSerializer { private _defaultUrlSerializer: DefaultUrlSerializer = new DefaultUrlSerializer(); diff --git a/frontend/app/gallery/gallery.service.ts b/frontend/app/gallery/gallery.service.ts index 932a012e..b9d5508f 100644 --- a/frontend/app/gallery/gallery.service.ts +++ b/frontend/app/gallery/gallery.service.ts @@ -10,7 +10,6 @@ import {Config} from '../../../common/config/public/Config'; import {ShareService} from './share.service'; import {NavigationService} from '../model/navigation.service'; import {SortingMethods} from '../../../common/entities/SortingMethods'; -import {QueryService} from '../model/query.service'; import {QueryParams} from '../../../common/QueryParams'; diff --git a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html index ad7922a3..0a5254a2 100644 --- a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html @@ -52,7 +52,7 @@ : {{VideoData.duration | duration}}
bit rate - : {{calcSize(VideoData.bitRate)}}/s + : {{VideoData.bitRate | fileSize}}/s
diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.html b/frontend/app/gallery/lightbox/lightbox.gallery.component.html index e5d3c5b1..fe4d27e4 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.html @@ -7,6 +7,8 @@
diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts index 41eb539b..9b3822cb 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts @@ -23,6 +23,8 @@ import {MediaDTO} from '../../../../common/entities/MediaDTO'; import {QueryParams} from '../../../../common/QueryParams'; import {GalleryService} from '../gallery.service'; import {PhotoDTO} from '../../../../common/entities/PhotoDTO'; +import {$e} from '@angular/compiler/src/chars'; +import {Utils} from '../../../../common/Utils'; export enum LightboxStates { Open = 1, @@ -70,7 +72,10 @@ 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; constructor(public fullScreenService: FullScreenService, private changeDetector: ChangeDetectorRef, @@ -119,6 +124,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit { if (this.activePhoto && this.queryService.getMediaStringId(this.activePhoto.gridPhoto.media) === photoStringId) { return; } + + this.setZoom(1); const photo = this.gridPhotoQL.find(i => this.queryService.getMediaStringId(i.gridPhoto.media) === photoStringId); if (!photo) { 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 @HostListener('window:resize', ['$event']) onResize() { @@ -185,6 +314,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit { } public showLigthbox(photo: MediaDTO) { + this.setZoom(1); this.controllersVisible = true; this.showControls(); this.status = LightboxStates.Open; @@ -275,6 +405,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit { } private hideLigthbox() { + this.setZoom(1); this.controllersVisible = false; this.status = LightboxStates.Closing; this.fullScreenService.exitFullScreen(); @@ -347,6 +478,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit { this.iPvisibilityTimer = window.setTimeout(() => { this.iPvisibilityTimer = null; this.infoPanelVisible = false; + this.checkZoomAndDrag(); }, 1000); const starPhotoPos = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.media); @@ -417,6 +549,8 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit { if (this.iPvisibilityTimer != null) { clearTimeout(this.iPvisibilityTimer); } + + this.checkZoomAndDrag(); } public fastForward() { diff --git a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html index 760192b4..16d3f659 100644 --- a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html @@ -2,23 +2,26 @@ diff --git a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts index c21b0b11..37aea49c 100644 --- a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts @@ -2,6 +2,7 @@ import {Component, ElementRef, Input, Output, OnChanges, ViewChild} from '@angul import {GridMedia} from '../../grid/GridMedia'; import {FixOrientationPipe} from '../../FixOrientationPipe'; import {MediaDTO} from '../../../../../common/entities/MediaDTO'; +import {DomSanitizer, SafeStyle} from '@angular/platform-browser'; @Component({ selector: 'app-gallery-lightbox-media', @@ -13,6 +14,8 @@ export class GalleryLightboxMediaComponent implements OnChanges { @Input() gridMedia: GridMedia; @Input() loadMedia = false; @Input() windowAspect = 1; + @Input() zoom = 1; + @Input() drag = {x: 0, y: 0}; @ViewChild('video') video: ElementRef; @@ -27,7 +30,14 @@ export class GalleryLightboxMediaComponent implements OnChanges { photoSrc: string = null; 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() {