forked from CIT/Vmeda.Online
		
	MOBILE-3950 image: Restore zoom on image viewer
This commit is contained in:
		
							parent
							
								
									8a97b0251d
								
							
						
					
					
						commit
						2ae55d04fb
					
				@ -2327,5 +2327,7 @@
 | 
			
		||||
  "core.years": "moodle",
 | 
			
		||||
  "core.yes": "moodle",
 | 
			
		||||
  "core.youreoffline": "local_moodlemobileapp",
 | 
			
		||||
  "core.youreonline": "local_moodlemobileapp"
 | 
			
		||||
  "core.youreonline": "local_moodlemobileapp",
 | 
			
		||||
  "core.zoomin": "local_moodlemobileapp",
 | 
			
		||||
  "core.zoomout": "local_moodlemobileapp"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ ion-slide {
 | 
			
		||||
::ng-deep {
 | 
			
		||||
 | 
			
		||||
    core-loading .core-loading-content {
 | 
			
		||||
        display: block;
 | 
			
		||||
        display: block !important;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -235,7 +235,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
            button.addEventListener('click', (e: Event) => {
 | 
			
		||||
                e.preventDefault();
 | 
			
		||||
                e.stopPropagation();
 | 
			
		||||
                CoreDomUtils.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId, true);
 | 
			
		||||
                CoreDomUtils.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            img.parentNode?.appendChild(button);
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,29 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-title>
 | 
			
		||||
            <h2>{{ title }}</h2>
 | 
			
		||||
        </ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times" slot="icon-only" aria-hidden=true></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<!-- @todo: zoom="true" maxZoom="2" . Now we need to use ionSlider? -->
 | 
			
		||||
<ion-content [scrollX]="true" [scrollY]="true" class="core-zoom-pane">
 | 
			
		||||
    <img [src]="image" [alt]="title" core-external-content [component]="component" [componentId]="componentId">
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-slides [options]="slidesOpts">
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <div class="swiper-zoom-container">
 | 
			
		||||
                <img [src]="image" [alt]="title" core-external-content [component]="component" [componentId]="componentId">
 | 
			
		||||
            </div>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
    </ion-slides>
 | 
			
		||||
</ion-content>
 | 
			
		||||
<ion-footer>
 | 
			
		||||
    <ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding">
 | 
			
		||||
        <ion-col size="auto">
 | 
			
		||||
            <ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-col>
 | 
			
		||||
        <ion-col></ion-col>
 | 
			
		||||
        <ion-col size="auto" *ngIf="slidesSwiper">
 | 
			
		||||
            <ion-button fill="clear" [attr.aria-label]="'core.zoomout' | translate" (click)="zoom(false)">
 | 
			
		||||
                <ion-icon name="fas-search-minus" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-col>
 | 
			
		||||
        <ion-col size="auto" *ngIf="slidesSwiper">
 | 
			
		||||
            <ion-button fill="clear" [attr.aria-label]="'core.zoomin' | translate" (click)="zoom(true)">
 | 
			
		||||
                <ion-icon name="fas-search-plus" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-col>
 | 
			
		||||
    </ion-row>
 | 
			
		||||
</ion-footer>
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
:host {
 | 
			
		||||
    .core-zoom-pane {
 | 
			
		||||
        height: 100%;
 | 
			
		||||
 | 
			
		||||
        img {
 | 
			
		||||
            max-width: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
ion-slides {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
img {
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-footer {
 | 
			
		||||
    background: var(--contrast-background);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,10 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
 | 
			
		||||
import { IonSlides } from '@ionic/angular';
 | 
			
		||||
import { ModalController, Translate } from '@singletons';
 | 
			
		||||
import { CoreMath } from '@singletons/math';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modal component to view an image.
 | 
			
		||||
@ -24,17 +25,46 @@ import { ModalController, Translate } from '@singletons';
 | 
			
		||||
    templateUrl: 'image.html',
 | 
			
		||||
    styleUrls: ['image.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreViewerImageComponent implements OnInit {
 | 
			
		||||
export class CoreViewerImageComponent implements OnInit, AfterViewInit {
 | 
			
		||||
 | 
			
		||||
    @Input() title?: string; // Modal title.
 | 
			
		||||
    @Input() image?: string; // Image URL.
 | 
			
		||||
    @ViewChild(IonSlides) protected slides?: IonSlides;
 | 
			
		||||
 | 
			
		||||
    @Input() title = ''; // Modal title.
 | 
			
		||||
    @Input() image = ''; // Image URL.
 | 
			
		||||
    @Input() component?: string; // Component to use in external-content.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID to use in external-content.
 | 
			
		||||
 | 
			
		||||
    slidesOpts = {
 | 
			
		||||
        slidesPerView: 1,
 | 
			
		||||
        centerInsufficientSlides: true,
 | 
			
		||||
        centerSlides: true,
 | 
			
		||||
        zoom: {
 | 
			
		||||
            maxRatio: 8,
 | 
			
		||||
            minRatio: 0.5, // User can zoom out to 0.5 only using pinch gesture.
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    protected zoomRatio = 1;
 | 
			
		||||
 | 
			
		||||
    slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any
 | 
			
		||||
 | 
			
		||||
    constructor(protected element: ElementRef<HTMLElement>) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.title = this.title || Translate.instant('core.imageviewer');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngAfterViewInit(): Promise<void> {
 | 
			
		||||
        this.slidesSwiper = await this.slides?.getSwiper();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
     */
 | 
			
		||||
@ -42,4 +72,33 @@ export class CoreViewerImageComponent implements OnInit {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Zoom In or Out.
 | 
			
		||||
     *
 | 
			
		||||
     * @param zoomIn: True to zoom in, false to zoom out.
 | 
			
		||||
     */
 | 
			
		||||
    zoom(zoomIn = true): void {
 | 
			
		||||
        const imageElement = this.element.nativeElement.querySelector('img');
 | 
			
		||||
 | 
			
		||||
        if (!this.slidesSwiper || !imageElement) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        zoomIn
 | 
			
		||||
            ? this.zoomRatio *= 2
 | 
			
		||||
            : this.zoomRatio /= 2;
 | 
			
		||||
 | 
			
		||||
        // Using 1 as minimum for manual zoom.
 | 
			
		||||
        this.zoomRatio = CoreMath.clamp(this.zoomRatio, 1, this.slidesOpts.zoom.maxRatio);
 | 
			
		||||
 | 
			
		||||
        if (this.zoomRatio > 1) {
 | 
			
		||||
            this.slidesSwiper.zoom.in();
 | 
			
		||||
 | 
			
		||||
            imageElement.style.transform =
 | 
			
		||||
                'translate3d(0px, 0px, 0px) scale(' + this.zoomRatio + ')';
 | 
			
		||||
        } else {
 | 
			
		||||
            this.slidesSwiper.zoom.out();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -347,5 +347,7 @@
 | 
			
		||||
    "years": "years",
 | 
			
		||||
    "yes": "Yes",
 | 
			
		||||
    "youreoffline": "You are offline",
 | 
			
		||||
    "youreonline": "You are back online"
 | 
			
		||||
    "youreonline": "You are back online",
 | 
			
		||||
    "zoomin": "Zoom In",
 | 
			
		||||
    "zoomout": "Zoom Out"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1787,14 +1787,12 @@ export class CoreDomUtilsProvider {
 | 
			
		||||
     * @param title Title of the page or modal.
 | 
			
		||||
     * @param component Component to link the image to if needed.
 | 
			
		||||
     * @param componentId An ID to use in conjunction with the component.
 | 
			
		||||
     * @param fullScreen Whether the modal should be full screen.
 | 
			
		||||
     */
 | 
			
		||||
    async viewImage(
 | 
			
		||||
        image: string,
 | 
			
		||||
        title?: string | null,
 | 
			
		||||
        component?: string,
 | 
			
		||||
        componentId?: string | number,
 | 
			
		||||
        fullScreen?: boolean,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        if (!image) {
 | 
			
		||||
            return;
 | 
			
		||||
@ -1808,7 +1806,7 @@ export class CoreDomUtilsProvider {
 | 
			
		||||
                component,
 | 
			
		||||
                componentId,
 | 
			
		||||
            },
 | 
			
		||||
            cssClass: fullScreen ? 'core-modal-fullscreen' : '',
 | 
			
		||||
            cssClass: 'core-modal-transparent',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -510,6 +510,27 @@ body.core-iframe-fullscreen ion-router-outlet {
 | 
			
		||||
    height: 100% !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.core-modal-transparent {
 | 
			
		||||
 | 
			
		||||
    ion-backdrop {
 | 
			
		||||
        backdrop-filter: blur(8px);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .modal-wrapper {
 | 
			
		||||
        backdrop-filter: blur(12px);
 | 
			
		||||
        --background: rgba(10, 10, 10, 0.2);
 | 
			
		||||
 | 
			
		||||
        ion-content {
 | 
			
		||||
            --background: transparent !important;
 | 
			
		||||
        }
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        @include position(0 !important, null, null, 0 !important);
 | 
			
		||||
        display: block;
 | 
			
		||||
        width: 100% !important;
 | 
			
		||||
        height: 100% !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.core-modal-force-on-top {
 | 
			
		||||
    z-index: 100000 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user