Home > Software engineering >  CSS animation using translateX instead of margin-left, is it possible?
CSS animation using translateX instead of margin-left, is it possible?

Time:06-08

Recently I ran into the video demonstrating difference between margin and transform animations. Unfortunately I can't remember video title or link. but basically it claimed that using transform:translateX() was way smoother than margin-left, especially on throttling mode.

I made an automatic slideshow with margin-left, and I'm trying to rewrite the animation using translateX, couldn't figure it out yet.
Any help will be appreciated.

body {
  margin: 0;
}

#slides {
  display: flex;
  overflow: hidden;
  height: 100vh;
  width: 100vw;
  margin: 0;
}

#slidewrapper {
  width: calc(6 * 100vw);
  /* you need to use calc for math */
  animation: slide 26s ease infinite;
}

.slide {
  /* float: left; <- unnecessary */
  height: 100vh;
  width: 100vw;
}

.landingphoto {
  height: 100vh;
  width: 100vw;
  object-fit: cover;
}

@keyframes slide {
  20% {
    margin-left: 0;
  }
  30% {
    margin-left: -100%;
  }
  50% {
    margin-left: -100%;
  }
  60% {
    margin-left: -200%;
  }
  70% {
    margin-left: -200%;
  }
  80% {
    margin-left: -300%;
  }
  90% {
    margin-left: -300%;
  }
}
<div id="slides">
  <div id="slidewrapper"></div>
  <div >
    <img src="https://source.unsplash.com/ULmaQh9Gvbg/1600x900" >
  </div>
  <div >
    <img src="https://source.unsplash.com/ggZuL3BTSJU/1600x900" >
  </div>
  <div >
    <img src="https://source.unsplash.com/yXpA_eCbtzI/1600x900" >
  </div>
  <div >
    <img src="https://source.unsplash.com/RyRpq9SUwAU/1600x900" >
  </div>
  <div >
    <img src="https://source.unsplash.com/BeOW_PJjA0w/1600x900" >
  </div>
</div>

CodePudding user response:

  • Don't use IDs, components are meant to be reusable. Use only Classes.
  • Speaking or reusability, don't hardcode animation in CSS. If you add only one image or remove one, you'll find yourself in need to painfully modify the CSS to adjust for the problem. Use JavaScript instead (in combination with CSS transform)

// Carousel (slides):

const carousel = (elCarousel) => {
  
  const timePause = 4000;
  const timeAnim = 1000;
  
  const elsSlides = elCarousel.children;
  const tot = elsSlides.length;
  let itv;
  let c = 0; // current slide index
  
  elCarousel.style.transition = `transform ${timeAnim}ms`;
  
  const anim = () => { elCarousel.style.transform = `translateX(${-c * 100}%)`; };
  const play = () => { itv = setInterval(next, timePause); };
  const stop = () => { clearInterval(itv); };
  const next = () => { c  = 1;c %= tot; anim(); };
  
  play();
  
};

// Apply carousel to desired elements:
document.querySelectorAll(".slidesWrapper").forEach(carousel);
/*QuickReset*/ * {margin:0; box-sizing: border-box;}

.slides {
  overflow: hidden;
}
.slidesWrapper {
  display: flex;
}
.slide {
  position: relative;
  flex: 0 0 100%;
  min-height: 200px;
}
.slide img {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<div >
  <div >
    <div >
      <img src="https://source.unsplash.com/ULmaQh9Gvbg/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/ggZuL3BTSJU/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/yXpA_eCbtzI/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/RyRpq9SUwAU/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/BeOW_PJjA0w/1600x900" >
    </div>
  </div>
</div>

You can have as many carousels as you want!

<div >
  <div >
    <div >
      <img src="https://source.unsplash.com/RyRpq9SUwAU/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/yXpA_eCbtzI/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/BeOW_PJjA0w/1600x900" >
    </div>
  </div>
</div>

PS: if you want to pause the animation on mouseenter and restart the animation on mouseleave simply add this two lines:

elCarousel.addEventListener("pointerenter", stop);
elCarousel.addEventListener("pointerleave", play);

CodePudding user response:

The basic problem is that the wrapper element in fact does not wrap the slides, it comes just before them.

The given code works because it sets the margin of the wrapper negative so everything is displaced by that amount.

We can't just make the margin settings into transform translateX settings therefore.

Instead this snippet makes the wrapper wrap the slides. It sets it to flex so the slides sit alongside each other and then it translates itself by multiples of 100vw in the animation.

You are right that translate can be smoother than setting the margin and this seems to be born out when using the browser mobile emulator for example.

body {
  margin: 0;
}

#slidewrapper {
  width: calc(6 * 100vw);
  /* you need to use calc for math */
  animation: slide 26s ease infinite;
  display: flex;
  overflow: hidden;
  height: 100vh;
  margin: 0;
}

.slide {
  /* float: left;  unnecessary */
  height: 100vh;
  width: 100vw;
  display: inline-block;
}

.landingphoto {
  height: 100vh;
  width: 100vw;
  object-fit: cover;
}

@keyframes slide {
  20% {
    transform: translateX(0);
  }
  30% {
    transform: translateX(-100vw);
  }
  50% {
    transform: translateX(-100vw);
  }
  60% {
    transform: translateX(-200vw);
  }
  70% {
    transform: translateX(-200vw);
  }
  80% {
    transform: translateX(-300vw);
  }
  90% {
    transform: translateX(-300vw);
  }
}
<div id="slides">
  <div id="slidewrapper">
    <div >
      <img src="https://source.unsplash.com/ULmaQh9Gvbg/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/ggZuL3BTSJU/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/yXpA_eCbtzI/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/RyRpq9SUwAU/1600x900" >
    </div>
    <div >
      <img src="https://source.unsplash.com/BeOW_PJjA0w/1600x900" >
    </div>
  </div>
</div>

CodePudding user response:

Here is my try, I have added some comments in the code

body {
  margin: 0;
}

#slides {
  display: flex;
  overflow: hidden;
  height: 100vh;
  width: 100vw;
  margin: 0;
}

#slidewrapper {
  width: calc(6 * 100vw);
  transition: all 2s; /* added this line */
}

.slide {
  height: 100vh;
  width: 100vw;
  animation: slide 16s ease infinite; /* move this line from #slidewrapper */
}

.landingphoto {
  height: 100vh;
  width: 100vw;
  object-fit: cover;
}

@keyframes slide {
  20% {
    transform: translateX(0); /* changed this line */
  }
  90% {
    transform: translateX(-400vw); /* changed this line */
  }
}
<div id="slides">
            <div id="slidewrapper"></div>
                <div >
                    <img src="https://source.unsplash.com/ULmaQh9Gvbg/1600x900">
                </div>
                <div >
                    <img src="https://source.unsplash.com/ggZuL3BTSJU/1600x900" >
                </div>
                <div >
                    <img src="https://source.unsplash.com/yXpA_eCbtzI/1600x900" >
                </div>
                <div >
                    <img src="https://source.unsplash.com/RyRpq9SUwAU/1600x900" >
                </div>
                <div >
                    <img src="https://source.unsplash.com/BeOW_PJjA0w/1600x900" >
                </div>
        </div>

is this what you mean? hope this help

  • Related