CLICK
← Docs

Using GSAP Flip during a Barba transition

Used technologies:

The problem

If we are resetting the current scroll position back to top and animate to a smaller/larger next container Flip may be a problem. Because Flip also remembers the current scroll position while animating this needs to be reset after a Barba transition.

The solution for this is to temporarily move the selected element outside the Barba containers and mimicking the same behaviour.
By doing so can do all container animations we'd like while maintaining the Flip element's scroll position.

The case

We want to animate a picture/element to the same element in the next page.
For example, my work-archive to work-detail animation, seen here.

The setup

By using Barba and GSAP ScrollSmoother the main content needs to be inside a `#smooth-wrapper` and a `#smooth-content` wrapper.

Barba + GSAP scrollsmoother implementation

We will move the Flip element inside the `#smooth-wrapper` element and mimick the current state.

Moving the Flip outside the container

First we'll clone the flip element inside the current DOM to make sure the page won't shift (layout shift).
Afterwards we move the Flip element outside the Barba container and immediately set the same state as the original one.

It is very important to make the Flip element absolute and Flip it without a duration. This makes sure to make an absolute copy in the same position but outside the Barba lifecycle.

// get the current Flip element from the clicked trigger
var myflip = data.trigger.querySelector('.flippingawesome');

// save a copy so the current DOM doesn't move! CLS..
var myflipcopy = myflip.cloneNode(true);
data.trigger.appendChild(myflipcopy); 

// get current Flip state and move it outside Barba
myflipstate = Flip.getState(myflip, {simple: true});
document.querySelector('#smooth-wrapper').appendChild(myflip);
Flip.to(myflipstate, {
  absolute: true,
  duration: 0,
});

Basically what happens now is that we have 2 the same elements positioned and styled the same way, one inside the current Barba container and the flip element outside the Barba containers so it won't be tempered with.

Animating your page-transition

Now the Flip element is set outside the Barba containers we can safely remove the current container and start our animation. See a basic setup here, with this being the most important part:

data.current.container.remove();

You'll now see that the Flip element will stay in place (perhaps with the right z-index CSS) and not move while you transition to the next page.

Moving the Flip element into the next container

First you need to create a defined slot of where the element should be placed. This is for Flip to know the correct sizing and spacing.
In my case I have the exact same element but with some different CSS classes and markup:

Barba GSAP Flip setup

After knowing where to place the Flip element we can move and animate it there.
Also I'll remove the original placeholder so that element won't be animated. This element still is used when reloading or animating to the page without Flip animation.

Flip.fit(myflip, data.next.container.querySelector('.fliphere'), { // get the next placeholder here
  scale: true, // scale so also the width/height will be adjusted to fit
  duration: 0.9,
  onComplete: function() {
    // after the animation move it here so it doesnt stay absolute/fixed outside the containers
    data.next.container.querySelector('.fliphere').prepend(myflip); 
  },
});
data.next.container.querySelector('.fliphere img').remove(); // remove the original one to hide that animation

Final result

Putting it all together:

import barba from '@barba/core';
import { Flip } from "./gsap.js";

var myflip;
var myflipstate;

barba.init({
  ...
  views: [{
    namespace: 'work-detail',
    beforeEnter(data) {
      // get the current Flip element from the clicked trigger
      var myflip = data.trigger.querySelector('.flippingawesome');

      // save a copy so the current DOM doesn't move! CLS..
      var myflipcopy = myflip.cloneNode(true);
      data.trigger.appendChild(myflipcopy); 
      
      // get current Flip state and move it outside Barba
      myflipstate = Flip.getState(myflip, {simple: true});
      document.querySelector('#smooth-wrapper').appendChild(myflip);
      Flip.to(myflipstate, {
        absolute: true,
        duration: 0,
      });
    },
    afterEnter(data) {
      Flip.fit(myflip, data.next.container.querySelector('.fliphere'), {
        scale: true,
        duration: 0.9,
        ease: "lvds",
        onComplete: function() {
          // after the animation move it here so it doesnt stay absolute/fixed outside the containers
          data.next.container.querySelector('.fliphere').prepend(myflip); 
        },
      });
      data.next.container.querySelector('.fliphere img').remove(); // remove the original one to hide that animation
    }
  }]
});



Contact_

Let's get in touch

And we'll see from there

Collaboration?
Start a project?
You name it...


Contact me at

hello@luukthe.dev