Ionic 5 image preview modal animation with Live example & source code
We are here with Ionic 5 image preview animation with examples and you will get all the source code too.
Have you ever wondered how social media apps create the animations to open and close the image preview?
We have tried to do the same. Have a look at the live demo, click on the image of the post and it will open the image preview modal with nice ionic 5 animation. We are using the same animation in the reverse to dismiss the modal.
When you try to swipe vertically on the image, it will close the modal and animate the image back to it’s related post.
You can also use image zoom in and out by pinch on a real device.
Live example
Setup ionic 5 image preview animation project
We are going to use our git repo to setup our project but you can use this code to your existing project and for that skip this step.
git clone https://github.com/SATPAL-BHARDWAJ/ionic-image-preview-animation.git npm install ionic serve
Override ionic modal animations?
You might be aware of ionic modal and already using it. It comes with pre-built animation for modal entry and dismissal.
We have overridden this animation to meet our purpose.
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { AnimationController, IonContent, ModalController } from '@ionic/angular'; import { MediaPreviewPage } from '../pages/media-preview/media-preview.page'; @Component({ selector: 'app-tab1', templateUrl: 'tab1.page.html', styleUrls: ['tab1.page.scss'] }) export class Tab1Page implements OnInit { @ViewChild('ionContent') ionContent: IonContent; posts: any = []; slideOpts = { initialSlide: 0, speed: 400 }; constructor( private modalCtrl: ModalController ) {} async ngOnInit() { this.posts = await fetch("assets/data/posts.json").then(resp => resp.json()); } async preview(event: any, file: string) { const body: HTMLElement = await this.ionContent.getScrollElement(); console.log({event}, {file}, body.clientHeight); const modal = await this.modalCtrl.create({ component: MediaPreviewPage, animated: true, enterAnimation: modalAnimation, leaveAnimation: leaveModalAnimation, componentProps: { image: file, position: event.clientY - event.layerY, viewport: (body.clientHeight/2) } }) return await modal.present(); } } const modalAnimation = (baseEl: HTMLElement) => { const AnimationC = new AnimationController; const baseAnimation = AnimationC.create(); const backdropAnimation = AnimationC.create(); backdropAnimation.addElement(baseEl.querySelector('ion-backdrop')); const wrapperAnimation = AnimationC.create(); wrapperAnimation.addElement(baseEl.querySelector('.modal-wrapper')) .keyframes([ { offset: 0, opacity: '0', transform: 'translateX(0%)' }, { offset: 1, opacity: '0.99', transform: 'translateX(0%)' } ]); backdropAnimation.fromTo('opacity', 0.01, 0.8); return baseAnimation .addElement(baseEl) .easing('ease-in-out') .duration(500) .beforeAddClass('show-modal') .addAnimation([backdropAnimation, wrapperAnimation]) } const leaveModalAnimation = (baseEl: HTMLElement) => { return modalAnimation(baseEl).duration(600).direction('reverse'); }
As you can see we are passing image, image position on the post, and whole ion-content (viewport)
height to the image preview modal.
So, let’s preview our image.
Image preview modal
On the image preview modal, we are showing our image center to the page with a light fade background.
I tried to use the offsetTop
property of mediaSlide (HTMLElement)
but it was taking 15 millisecond to get the actual value. That’s why I used the viewport height and passed it from the post listing page to this preview modal.
We simply translate our image position vertically to the position where it was on the post listing page. Then after 50ms it will remove this translation and the image will be back to the required position that is the center of the modal page.
We are also using an ionic gesture to get the vertical swipe movement to dismiss the modal.
import { AfterViewInit, Component, ElementRef, Input, OnInit, Renderer2, ViewChild } from '@angular/core'; import { GestureController, ModalController } from '@ionic/angular'; @Component({ selector: 'app-media-preview', templateUrl: './media-preview.page.html', styleUrls: ['./media-preview.page.scss'], }) export class MediaPreviewPage implements OnInit, AfterViewInit { @ViewChild('mediaSlide') mediaSlide : ElementRef; slideOpts = { initialSlide: 0, speed: 400, loop: false, zoom: { minRatio: 1, maxRatio: 3, toggle: true, containerClass :'swiper-zoom-container', zoomedSlideClass: 'swiper-slide-zoomed' } }; @Input() image: string; @Input() position: number; @Input() viewport: number; translateY: number; constructor( private modalCtrl: ModalController, private renderer: Renderer2, private gestureCtrl: GestureController ) { } ngOnInit() { } ionViewDidEnter() { const gesture = this.gestureCtrl.create({ gestureName: 'drag-image', el: this.mediaSlide.nativeElement, threshold: 70, direction: 'y', onStart: () => { console.log('start!'); this.dismiss(); }, }); gesture.enable(); } ngAfterViewInit() { this.translateY = this.position - (this.viewport*84/100); console.log( this.position, this.viewport*84/100, this.translateY ); if ( this.translateY < -30 || this.translateY > 30 ) { this.renderer.setStyle(this.mediaSlide.nativeElement, 'transform', `translateY(${this.translateY}px) scale3d(0.99, 0.99, 1)`); this.renderer.setStyle(this.mediaSlide.nativeElement, 'transition', '0.5s ease-in-out'); setTimeout(() => { this.renderer.removeStyle(this.mediaSlide.nativeElement, 'transform'); }, 50); } } dismiss() { if ( this.translateY < -30 || this.translateY > 30 ) { setTimeout(() => { this.renderer.setStyle(this.mediaSlide.nativeElement, 'transform', `translateY(${this.translateY}px) scale3d(0.94, 0.94, 1)`); }, 50); } this.modalCtrl.dismiss(); } }
Here it is, ionic 5 image preview animation with live example and source code.
See you in the next learning journey.