Ionic

Ionic 5 image preview modal animation with Live example & source code

Share your learning

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.

Satpal

Recent Posts

How to Switch PHP Versions in XAMPP Easily: Managing Multiple PHP Versions on Ubuntu

Today we are going to learn about managing multiple PHP versions on ubuntu with xampp.…

1 year ago

How to Use Coding to Improve Your Website’s SEO Ranking?

Let's understand about how to use coding to improve your website's SEO. In today’s computerized…

1 year ago

Most Important Linux Commands for Web Developers

Let's understand the most important linux commands for web developers. Linux, as an open-source and…

1 year ago

Top 75+ Laravel Interview Questions Asked by Top MNCs

Today we are going to discuss top 75+ Laravel interview questions asked by top MNCs.Laravel,…

2 years ago

Mailtrap Integration for Email Testing with Laravel 10

Today we will discuss about the Mailtrap integration with laravel 10 .Sending and receiving emails…

2 years ago

Firebase Cloud Messaging (FCM) with Ionic 6: Push Notifications

Today we are going to integrate FCM (Firebase Cloud Messaging) push notifications with ionic application.Firebase…

2 years ago