DoneDeal
DoneDeal

Reputation: 159

How to create a smooth background image transition in React?

I have a header, whose className changes depending on State. Each class has a different background image, specified in the CSS. Everything works fine, but the transitions are quite abrupt without a fade-in effect.

I wrote:

.jumbotron-img-1{
    background-image: url("/images/myImg1.jpg");
    transition: all 1s ease-in-out;

It works, but it's ugly. There is a zoom, and a distortion of the image before it shows up in its final form. I've watched some tutorials on Google, but nothing was simple and to the point for background-image transition in pure CSS or React.

Any help would be greatly appreciated, thanks!

Upvotes: 4

Views: 13301

Answers (2)

Gaurav Saraswat
Gaurav Saraswat

Reputation: 1383

background-image is not an animatable property. I feel what best serves your purpose is to render multiple headers with all the classnames available stacked over each other with position: absolute; relative to common parent and make only one of them visible using opacity property based on which classname is active in your state and use transition on opacity

Sample working code:

render() {
   const {imgClassList} = this.props;
   const {activeimgClass} = this.state;
   return (
     <div className="header-container">
       {imgClassList.map(imgClass => {
         return (
           <div 
             className={`header ${imgClass} ${(imgClass === activeimgClass)? 'active' : ''}`}
           />)
       })}
     </div>
   )
}

And css be something like:

.header-container {
  position: relative;
}
.header{
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0
  transition: opacity 1s ease-in-out;
}
.header.active {
  opacity: 1
}
.img-1 {
  background:url('images/img-1')
}
.img-2 {
  background: url('images/img-2')
} ... and so on

Upvotes: 5

jaredgorski
jaredgorski

Reputation: 540

There's no good way to transition a background image using CSS because it's not an animatable property, per the CSS spec. One way to do this is to just have multiple images on top of one another, each containing a different one of the images you'd like to display, and then cycle through them by transitioning them to opacity: 0 and changing their z-index order.

I made a quick demo showing how you can achieve smooth changes by manipulating opacity and z-index. In pure Javascript, this is done by simply adjusting the styles with DOM manipulation and using setTimeout().

Of course in React you don't want to be doing DOM manipulation, so you can experiment with multiple classes with different opacity levels and transitions to accomplish this. There also seems to be a React component that enables all types of transitions: https://reactcommunity.org/react-transition-group/css-transition

Check out the Javascript solution demo to see how changing the opacity can get a crossfade effect on images:

function backgroundScheduler_1() {
  setTimeout(() => {
      document.querySelector(".img1").style.opacity = 0;
      document.querySelector(".img2").style.opacity = 1;
      document.querySelector(".img3").style.opacity = 1;
      order(["-3", "-1", "-2"], () => { backgroundScheduler_2() }, 1000);
  }, 3000);
}

function backgroundScheduler_2() {
  setTimeout(() => {
      document.querySelector(".img1").style.opacity = 1;
      document.querySelector(".img2").style.opacity = 0;
      document.querySelector(".img3").style.opacity = 1;
      order(["-2", "-3", "-1"], () => { backgroundScheduler_3() }, 1000);
  }, 3000);
}

function backgroundScheduler_3() {
  setTimeout(() => {
      document.querySelector(".img1").style.opacity = 1;
      document.querySelector(".img2").style.opacity = 1;
      document.querySelector(".img3").style.opacity = 0;
      order(["-1", "-2", "-3"], () => { backgroundScheduler_1() }, 1000);
  }, 3000);
}

function order(array, callback, time) {
  setTimeout(() => {
      document.querySelector(".img1").style.zIndex = array[0];
      document.querySelector(".img2").style.zIndex = array[1];
      document.querySelector(".img3").style.zIndex = array[2];
      callback();
  }, time);
}

backgroundScheduler_1();
.background-image {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 1;
  transition: 1s;
}

.img1 {
  z-index: -1;
}

.img2 {
  z-index: -2;
}

.img3 {
  z-index: -3;
}
<div class="background-container">
  <img class="background-image img1" src="https://placeimg.com/640/640/nature"></img>
  <img class="background-image img2" src="https://placeimg.com/640/640/animals"></img>
  <img class="background-image img3" src="https://placeimg.com/640/640/tech"></img>
  
  <h2 style="color: white;">WOW!</h2>
</div>

I checked NPM momentarily and didn't see anything that promises this exact functionality. Hope this helps!

Upvotes: 2

Related Questions