javascriptjquerycsscss-transitionstransition

How to create staggered transition effects for slideshow?


I've seen a number of examples of jQuery plugins that create slideshows with lots of different styles of effects when the images transition from one to another.

I have been playing with this over the last day or so, and have some nice working examples of a slideshow using JQuery and would like start to use some of the more exotic transitions rather than simple sliding images in a list from left to right, or fading one image out and another in.

I want to lean how to do this myself, rather than use plugins, and wondered if anyone has an simple example of how I would transition from one image to another and have a block effect (or other other fancy effects), where one image is replaced by another made up of smaller blocks that fade in etc?

Thanks


Solution

  • Slide gallery with staggered effect

    Slide gallery with staggered effect

    Here's a way using vanilla JS, and the Animation API

    // Utility functions:
    const el = (sel, par = document) => par.querySelector(sel);
    const els = (sel, par = document) => par.querySelectorAll(sel);
    const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
    const repeat = (n, cb) => [...Array(n)].forEach((_, i) => cb(i));
    
    // Cut Gallery
    const cuts = 8;
    const elGallery   = el('#gallery');
    const images = [
      "https://picsum.photos/id/40/600/400",
      "https://picsum.photos/id/36/600/400",
      "https://picsum.photos/id/39/600/400",
      "https://picsum.photos/id/133/600/400",
      "https://picsum.photos/id/120/600/400"
    ];
    const tot = images.length;
    let canAnimate = true;
    let curr = 0;
    
    // Preload images
    images.forEach((src) => { 
      const img = new Image();
      img.src = src;
    });
    
    // Creates a new set of 5 DIV:
    const createSlides = () => {
      elGallery.innerHTML = "";
      repeat(cuts, (i) => {
        const pct = 100 / (cuts - 1) * i;
        const div = elNew("div", {
          className: "slide",
          style: `background: no-repeat url(${images[curr]}) ${pct}% center / ${cuts * 100}% auto;`
        });
        elGallery.append(div);
      })
    };
    
    const getAnimation = (index, i) => {
      const fxs = [
        [{ translate: `0% -100%` }],
        [{ translate: `0% 100%` }],
        [{ translate: `${-100 * (i + 1)}% 0%`}],
        [{ translate: `${100 * (cuts - i)}% 0%` }],
        [{ opacity: 0, scale: 1.2 }],
        [{ rotate: "45deg", opacity: 0, translate: "0 -100%" }],
      ];
      return fxs[index];
    };
    
    let totAnimations = 6;
    let currentAnimation = 0;
    const anim = () => {
      canAnimate = false;
      elGallery.style.backgroundImage = `url(${images[curr]})`;
      els(".slide", elGallery).forEach((elSlide, i) => {
        elSlide.animate(getAnimation(currentAnimation, i), {
          duration: 500,
          delay: i * 80,
          fill: "forwards",
          easing: "cubic-bezier(0.5, 0, 1, 0.7)"
        }).addEventListener("finish", () => {
          if (i < cuts - 1) return;
          createSlides();
          canAnimate = true;
        });
      });
      currentAnimation += 1;
      currentAnimation %= totAnimations;
    };
    
    // INIT
    
    createSlides();
    
    el("#next").addEventListener("click", () => {
      if (!canAnimate) return;
      curr = ++curr % tot; // Increment counter
      anim();
    });
    * { margin: 0; box-sizing: border-box; }
    
    #gallery{
      display: flex;
      overflow: hidden;
      height: 200px;
      background: no-repeat center / 100%;
    
      & .slide {
        position: relative;
        flex: 1;
      }
    }
    <button id="next">Show next effect</button>
    Click to see all random FX!
    <div id="gallery"></div>