From 3f90e9d1db74f86fc95bcea8243899b165d99fff Mon Sep 17 00:00:00 2001 From: trivernis Date: Sun, 31 Oct 2021 15:12:25 +0100 Subject: [PATCH] Add zoom to gallery view Signed-off-by: trivernis --- mediarepo-ui/src/app/app.module.ts | 2 + .../content-aware-image.component.ts | 2 - .../file-gallery/file-gallery.component.html | 15 +++++- .../file-gallery/file-gallery.component.scss | 29 +++++++++++ .../file-gallery/file-gallery.component.ts | 52 +++++++++++++++---- .../search-page/search-page.component.html | 2 +- .../home/search-page/search-page.component.ts | 38 -------------- 7 files changed, 87 insertions(+), 53 deletions(-) diff --git a/mediarepo-ui/src/app/app.module.ts b/mediarepo-ui/src/app/app.module.ts index cc10402..aeb3a44 100644 --- a/mediarepo-ui/src/app/app.module.ts +++ b/mediarepo-ui/src/app/app.module.ts @@ -42,6 +42,7 @@ import {BlockUIModule} from "primeng/blockui"; import {PanelModule} from "primeng/panel"; import {DragDropModule} from "@angular/cdk/drag-drop"; import { ContentAwareImageComponent } from './components/content-aware-image/content-aware-image.component'; +import {MatSliderModule} from "@angular/material/slider"; @NgModule({ declarations: [ @@ -90,6 +91,7 @@ import { ContentAwareImageComponent } from './components/content-aware-image/con BlockUIModule, PanelModule, DragDropModule, + MatSliderModule, ], providers: [], bootstrap: [AppComponent] diff --git a/mediarepo-ui/src/app/components/content-aware-image/content-aware-image.component.ts b/mediarepo-ui/src/app/components/content-aware-image/content-aware-image.component.ts index 183e301..e0a113a 100644 --- a/mediarepo-ui/src/app/components/content-aware-image/content-aware-image.component.ts +++ b/mediarepo-ui/src/app/components/content-aware-image/content-aware-image.component.ts @@ -22,10 +22,8 @@ export class ContentAwareImageComponent { public adjustSize(image: HTMLImageElement, imageContainer: HTMLDivElement): void { const containerHeight = Math.abs(imageContainer.clientHeight); const containerWidth = Math.abs(imageContainer.clientWidth); - console.log(containerHeight, ',', containerWidth); const imageRelativeHeight = image.height / containerHeight; const imageRelativeWidth = image.width / containerWidth; - console.log(imageRelativeHeight, ',', imageRelativeWidth); this.scaleWidth = imageRelativeWidth > imageRelativeHeight; } } diff --git a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.html b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.html index f4a3a4a..b0ef55d 100644 --- a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.html +++ b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.html @@ -4,11 +4,22 @@
-
+
- +
+ + +
+
+
+ +
+
diff --git a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.scss b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.scss index 7eec01d..e21ad47 100644 --- a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.scss +++ b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.scss @@ -13,6 +13,7 @@ height: 100%; width: 100%; position: relative; + user-select: none; } app-file-gallery-entry, .file-item { @@ -28,6 +29,7 @@ app-file-gallery-entry { .file-full-view { width: 100%; height: 100%; + overflow: hidden; } .file-full-view-inner { @@ -63,3 +65,30 @@ app-content-aware-image { margin: auto; } } + +.image-drag-container, .image-scale-container { + height: 100%; + width: 100%; +} + +.image-scale-container { + display: block; +} + +.zoom-slider { + position: absolute; + display: flex; + flex-direction: column; + right: 1em; + bottom: 1em; + z-index: 10; + opacity: 0.5; + padding: 1em 0.5em; + transition-duration: 0.2s; +} + +.zoom-slider:hover { + opacity: 1; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 1em; +} diff --git a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.ts b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.ts index e4fce1f..ee8d354 100644 --- a/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.ts +++ b/mediarepo-ui/src/app/components/file-gallery/file-gallery.component.ts @@ -11,6 +11,7 @@ import {FileService} from "../../services/file/file.service"; import {SafeResourceUrl} from "@angular/platform-browser"; import {Selectable} from "../../models/Selectable"; import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling"; +import {CdkDrag, CdkDragMove, DragRef, Point} from "@angular/cdk/drag-drop"; @Component({ selector: 'app-file-gallery', @@ -27,10 +28,13 @@ export class FileGalleryComponent implements OnChanges, OnInit { entries: Selectable[] = []; @ViewChild("virtualScroll") virtualScroll!: CdkVirtualScrollViewport; + @ViewChild("scaledImage") scaledImage: ElementRef | undefined; + @ViewChild("imageDragContainer") imageDragContainer: ElementRef | undefined; public selectedFile: Selectable | undefined; fileContentUrl: SafeResourceUrl | undefined; - scaleWidth = false; + public imageZoom = 1; + public imagePosition = {x: 0, y: 0}; constructor(private fileService: FileService) { } @@ -42,6 +46,7 @@ export class FileGalleryComponent implements OnChanges, OnInit { */ async onEntrySelect(entry: Selectable) { if (entry) { + this.resetImage(); this.selectedFile?.unselect(); entry.select(); this.selectedFile = entry; @@ -58,7 +63,8 @@ export class FileGalleryComponent implements OnChanges, OnInit { async loadSelectedFile() { if (this.selectedFile) { this.fileContentUrl = undefined; - this.fileContentUrl = await this.fileService.readFile(this.selectedFile.data); + this.fileContentUrl = await this.fileService.readFile( + this.selectedFile.data); } } @@ -69,8 +75,10 @@ export class FileGalleryComponent implements OnChanges, OnInit { } public async ngOnChanges(changes: SimpleChanges): Promise { - this.entries = this.files.map(f => new Selectable(f, f.hash == this.selectedFile?.data.hash)); - const selectedIndex = this.files.findIndex(f => f.hash === this.selectedFile?.data.hash); + this.entries = this.files.map( + f => new Selectable(f, f.hash == this.selectedFile?.data.hash)); + const selectedIndex = this.files.findIndex( + f => f.hash === this.selectedFile?.data.hash); if (!this.selectedFile || selectedIndex < 0) { await this.onEntrySelect(this.getPreselectedEntry() ?? this.entries[0]) @@ -111,6 +119,11 @@ export class FileGalleryComponent implements OnChanges, OnInit { } } + public resetImage() { + this.imageZoom = 1; + this.imagePosition = {x: 0, y: 0}; + } + @HostListener("window:keydown", ["$event"]) private async handleKeydownEvent(event: KeyboardEvent) { switch (event.key) { @@ -120,12 +133,33 @@ export class FileGalleryComponent implements OnChanges, OnInit { case "ArrowLeft": await this.previousItem(); break; + case "Escape": + this.resetImage(); + break; + } + } + + @HostListener("mousewheel", ["$event"]) + private handleScroll(event: any) { + const delta = event.wheelDelta ?? event.detail + + if (delta > 0) { + this.imageZoom += 0.2 + if (this.imageZoom > 4) { + this.imageZoom = 4; + } + } else if (delta < 0) { + this.imageZoom -= 0.2 + if (this.imageZoom < 0.5) { + this.imageZoom = 0.5; + } } } private getPreselectedEntry(): Selectable | undefined { if (this.preselectedFile) { - const entry = this.entries.find(e => e.data.hash == this.preselectedFile?.hash); + const entry = this.entries.find( + e => e.data.hash == this.preselectedFile?.hash); if (entry) { return entry; } @@ -133,10 +167,8 @@ export class FileGalleryComponent implements OnChanges, OnInit { return undefined; } - public adjustImageSize(fullImage: HTMLImageElement, imageContainer: HTMLDivElement): void { - const containerRatio = imageContainer.clientHeight / imageContainer.clientWidth; - const imageAdjHeight = fullImage.height / containerRatio; - const imageAdjWidth = fullImage.width * containerRatio; - this.scaleWidth = imageAdjWidth > imageAdjHeight; + public onDragMoved($event: CdkDragMove): void { + this.imagePosition.x += $event.delta.x; + this.imagePosition.y += $event.delta.y; } } diff --git a/mediarepo-ui/src/app/pages/home/search-page/search-page.component.html b/mediarepo-ui/src/app/pages/home/search-page/search-page.component.html index d1ef5e6..d2fe202 100644 --- a/mediarepo-ui/src/app/pages/home/search-page/search-page.component.html +++ b/mediarepo-ui/src/app/pages/home/search-page/search-page.component.html @@ -26,7 +26,7 @@ (fileMultiselectEvent)="onFileMultiSelect($event)" > diff --git a/mediarepo-ui/src/app/pages/home/search-page/search-page.component.ts b/mediarepo-ui/src/app/pages/home/search-page/search-page.component.ts index 32203e5..510a43b 100644 --- a/mediarepo-ui/src/app/pages/home/search-page/search-page.component.ts +++ b/mediarepo-ui/src/app/pages/home/search-page/search-page.component.ts @@ -86,19 +86,6 @@ export class SearchPageComponent implements OnInit { event.source.deselectAll(); } - async openFile(file: File) { - if (this.openingLightbox) { - return; - } - this.openingLightbox = true; - try { - await this.openLightbox(file); - } catch (err) { - this.errorBroker.showError(err); - } - this.openingLightbox = false; - } - async openGallery(preselectedFile: File) { this.preselectedFile = preselectedFile; this.showGallery = true; @@ -108,29 +95,4 @@ export class SearchPageComponent implements OnInit { this.preselectedFile = preselectedFile; this.showGallery = false; } - - private async openLightbox(file: File): Promise { - let url = await this.fileService.readFile(file); - - let albums = [ - { - src: url as string, - caption: file.name ?? file.comment, - thumb: url as string, - } - ]; - this.lightbox.open(albums, 0, { - disableScrolling: true, - showImageNumberLabel: false, - showDownloadButton: true, - centerVertically: true, - }); - const lighboxSubscription = this.lightboxEvent.lightboxEvent$.subscribe( - (event: any) => { - if (event?.id == LIGHTBOX_EVENT.CLOSE) { - lighboxSubscription.unsubscribe(); - URL?.revokeObjectURL(url as string); - } - }) - } }