Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared element transition using ReactJS

In Android, shared element transition allows 2 exact same elements existing in both pages to link together when transitioning pages, just like the album art in the gif shown below:

Android shared element transition

I wonder if it is possible to achieve the same kind of transition with ReactJS between classes. If so, any examples? If not, what about with jQuery?

like image 515
thousight Avatar asked Jun 01 '17 17:06

thousight


Video Answer


1 Answers

You can do this transition almost entirely with the CSS transform property. React JS is all about manipulating the DOM, but you don't need to do that here much.

The animation:

  1. Hides the text content of the small panel.
  2. Scales the picture and text background to fill full screen.
  3. Puts in the new text content.

Of those 1 and 3 are easy with React, so you only really need the transition animation.

Here is a very very basic example using no JS at all:

body {
  background-color: #ccc;
}

.card {
  width: 150px;
  padding: 0;
  margin: 0;
  background-color: #fff;
  position: absolute;
  top: 0: left: 0;
  z-index: 1;
  
  /* Transition properties mean changes to them are animated */
  transition-property: transform;
  transition-timing-function: ease-in-out;
  transition-duration: 500ms;
  transform-origin: top left;
}

.card>img {
  width: 150px;
  height: 150px;
  margin: 0;
  padding: 0;
}

.card>.content {
  width: 150px;
  height: 50px;
  background-color: #fff;
  margin: 0;
}


/* This is only for the purposes of this demo.
 * In production you'd have an underlying grid layout and JS to figure out the position */
.card:nth-of-type(2) {
  left: 175px;
}

.card:nth-of-type(3) {
  top: 230px;
}

.card:nth-of-type(4) {
  top: 230px;
  left: 175px;
}


/* On hover transform the card to full size and translate it to the top left
 * Note that translate comes before scale. */
.card:nth-of-type(1):hover {
  transform: scale(2.1667);
  z-index: 2;
}

.card:nth-of-type(2):hover {
  transform: translate(-175px, 0) scale(2.1667);
  z-index: 2;
}

.card:nth-of-type(3):hover {
  transform: translate(0, -230px) scale(2.1667);
  z-index: 2;
}

.card:nth-of-type(4):hover {
  transform: translate(-175px, -230px) scale(2.1667);
  z-index: 2;
}
<div class="card">
  <img src="http://via.placeholder.com/325/F50057/ffffff">
  <div class="content"></div>
</div>

<div class="card">
  <img src="http://via.placeholder.com/325/F44336/ffffff">
  <div class="content"></div>
</div>

<div class="card">
  <img src="http://via.placeholder.com/325/1DE9B6/000000">
  <div class="content"></div>
</div>

<div class="card">
  <img src="http://via.placeholder.com/325/FFEB3B/000000">
  <div class="content"></div>
</div>

The basic trick is to use CSS transform with translate and scale - these properties can be handled by the graphics card and so keep animations smooth even on mobile.

Note that the CSS is rather clunky - I've done it like that just to show that it can be done with pure CSS. In practice you're going to want some JS to set the offset properties, hook up a click event, etc.

Another trick (which I haven't done here) is to scale the animation backwards - start with the full size control and translate/scale it down into the position it appears to start in. When the user clicks on it remove the transform - that saves the browser from having to recalculate the full sized object's DOM before starting the animation.

like image 187
Keith Avatar answered Sep 29 '22 16:09

Keith