3

Not the best title, I admit, but I'll try to compensate explaining.

I would like to create a gallery, similar to a Carousel (but not quite), in which its images are quickly navigated automatically after clicking an element, like a spinner.

All images are randomized by the server-side language and loaded in the DOM, inline, having only the first one visible and rest hidden. My idea was to use CSS animations to spin them to the left and it worked very nicely, even though I couldn't reproduce it in the Fiddle.

Since I'm forced to duplicate the code, here it goes:

$( document ).ready( function() {

  $( '#randomizer .wheel' ).on( 'click', function() {

      /**
       * @internal
       *
       * 70 is the upper bound of the range and 50 the lower bound
       *
       * @see https://stackoverflow.com/a/7228322/5613506
       */
      var time = Math.floor( Math.random() * ( 70 - 50 + 1 ) + 50 );

      var selector = $( '#randomizer .wheel div, #images > div' );

      selector.addClass( 'spinning' )
              .css( '--spinner-animation-time', time + 'ms' )
              .on( 'animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function() {
                  selector.removeClass( 'spinning' )
              });
  });
});
:root {
  --spinner-animation-time: 200ms;
}

.row {
    overflow: hidden;
    width: 33.333333%;
}

#randomizer {
    height: 12vh;
    margin-top: 0.1vh;
    position: relative;
    z-index: 4;
}

    #randomizer .wheel {
        -webkit-border-top-left-radius: 0;
        -webkit-border-top-right-radius: 0;
        -moz-border-radius-topleft: 0;
        -moz-border-radius-topright: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-top: none !important;
        padding: 2vh 0 1.5vh 0;
    }

    #randomizer .wheel > div {
        height: 8.4vh;
        overflow: hidden;
        position: relative;
        -webkit-transform: translate3d( 0, 0, 0 );  /* Chrome, Opera 15+, Safari 3.1+ */
            -ms-transform: translate3d( 0, 0, 0 );  /* IE 9 */
                transform: translate3d( 0, 0, 0 );  /* Firefox 16+, IE 10+, Opera */
        width: 95vw; /** 4 white blocks with their black borders visible */
    }

        #randomizer .wheel img {
            cursor: pointer;
            object-fit: cover;
            position: relative;
            top: 0.1vh;
            width: 55vw;
        }

            /*#randomizer .wheel img:hover {
                animation: randomizer 100ms 10;
            }*/
            
#images {
  align-items: center;
  border: 1px solid #000;
  display: inline-flex;
  justify-content: center;
  height: 300px;
  margin-top: 6vh;
  position: absolute;
  width: 450px;
  z-index: 3;
}

#images div {
  display: inline-flex;
  height: 100%;
  overflow: hidden;
  width: 100%;
}

    #images img {
      /*clip-path: inset( 9.2vh 1.2vw 24.5vh 4.7vw );*/
      /*transform: scale( 0.8, 1 );*/
      height: 100%;
      width: 100%;
    }

    #images .spinning img {
      animation: randomizer var(--spinner-animation-time) 25;
    }

#randomizer .wheel > div.spinning img {
  animation: randomizer var(--spinner-animation-time) 25;
}

@keyframes randomizer {
  100% {
    -webkit-transform: translateX( -33.3333% );  /* Chrome, Opera 15+, Safari 3.1+ */
        -ms-transform: translateX( -33.3333% );  /* IE 9 */
            transform: translateX( -33.3333% );  /* Firefox 16+, IE 10+, Opera */
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="images">
    <div>
        <img src="http://brightcove04.o.brightcove.com/1362235914001/1362235914001_4806146059001_video-still-for-video-4805957335001.jpg?pubId=1362235914001" />
        <img src="https://oup.useremarkable.com/production/images/uploads/3315/original/email-symbol-thumbnail.jpg?1473424084" />
        <img src="https://i.ytimg.com/vi/xYrRq5U5kcc/maxresdefault.jpg" />
        <img src="https://oup.useremarkable.com/production/images/uploads/2998/original/plus-sign-thumbnail.jpg?1471959737" />
        <img src="https://oup.useremarkable.com/production/images/uploads/2816/original/crossword-thumbnail.jpg?1471959676" />
    </div>
</div>
  
<div class="row" id="randomizer">

    <div class="wheel">
        <div>
            <img src="https://i.imgur.com/ikch8mX.jpg" />
        </div>
    </div>

</div>

Even though it's bugged only in this demo (let's emphasize that), probably because I couldn't extract everything I've done in my real code, I believe it should suffice for you to understand the concept.

However, this is only a simple animation, it goes from 0% to 100% and it's done. Even though the images' list is shuffled, the animation will start showing the first image and end showing the first image.

And I would like to really spin all images, stopping randomly at one of them. From the "chosen" image I could read some data attributes to use as AJAX parameters, but that's beside the point.

I tried to generate a random animation speed value, with something I learned today (CSS variables) but in the end, all I've got was a way to simulate the strength a person would be tapping the spinner, harder or lighter, spinning for more or less time.

How could I accomplish that?

user5613506
  • 215
  • 1
  • 11

1 Answers1

2

I came up with a solution that I believe would help you with your problem.

Since much of animations are visual tricks and, as you said yourself, the animation goes from the first image to the first image once again, all you'd need is move the DOM nodes around.

While the animation is running, you pick a random image node, remove it from its position and when the animation ends you re-add it to the top. With a fast enough animation definition, the human eye will hardly see the missing image.

$( document ).ready( function() {

    $( '#randomizer .wheel' ).on( 'click', function() {
 
   var $images = $( '#images div' );
      
       /**
         * @internal
         *
         * Choosing a random image from all available. When the spinning
         * animation is finished we remove it from it is and then prepend to
         * the Images container.
         *
         * Since the animation goes from the first image to the first image
         * we have the visual illusion of randomness ;)
         */
         var chosen = $images.find( 'img' ).eq( Math.floor( Math.random() * 10 ) );

         var selector = $( '#randomizer .wheel div, #images > div' );

         selector.addClass( 'spinning' )
                 .on( 'animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd', function() {

             chosen.remove().prependTo( $images );

             selector.removeClass( 'spinning' )
         });
     });
});
body {
    margin: 2% auto;
    text-align: center;
}

.row {
    overflow: hidden;
}

#randomizer {
    display: flex;
    height: 12vh;
    justify-content: center;
}

    #randomizer .wheel {
        -webkit-border-top-left-radius: 0;
        -webkit-border-top-right-radius: 0;
        -moz-border-radius-topleft: 0;
        -moz-border-radius-topright: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-top: none !important;
        padding: 2vh 0 1.5vh 0;
    }

    #randomizer .wheel > div {
        height: 9.2vh;
        overflow: hidden;
        position: relative;
        -webkit-transform: translate3d( 0, 0, 0 );  /* Chrome, Opera 15+, Safari 3.1+ */
            -ms-transform: translate3d( 0, 0, 0 );  /* IE 9 */
                transform: translate3d( 0, 0, 0 );  /* Firefox 16+, IE 10+, Opera */
        width: 15.9vw; /** 4 white blocks with their black borders visible */
    }

        #randomizer .wheel img {
            cursor: pointer;
            object-fit: cover;
            position: relative;
            top: 0.1vh;
            width: 55vw;
        }
            
#images {
    display: -webkit-inline-box;
    display: -ms-inline-flexbox;
    display: inline-flex;

    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;

    top: 15vh;
}

#images div {
    display: -webkit-inline-box;
    display: -ms-inline-flexbox;
    display: inline-flex;

    height: 250px; /** Images' Height — required by Firefox */
    overflow: hidden;
    width: 350px; /** Images' Width */
}

    #images img {
        height: 100%;
        width: 100%;

        /**
         * @see https://stackoverflow.com/a/42240538/5613506
         */
        will-change: transform;
    }

    #images .spinning img {
        animation-name: images;
        animation-duration: .6s;
        animation-iteration-count: 3;
    }

#randomizer .wheel > div.spinning img {
    -webkit-animation: randomizer .2s 8;
            animation: randomizer .2s 8;
}

@keyframes images {
      0% { transform: translateX( 0px );     }
     10% { transform: translateX( -350px );  }
     20% { transform: translateX( -700px ); }
     30% { transform: translateX( -1050px ); }
     40% { transform: translateX( -1400px ); }
     50% { transform: translateX( -1750px ); }
     60% { transform: translateX( -2100px ); }
     70% { transform: translateX( -2450px ); }
     80% { transform: translateX( -2800px ); }
     90% { transform: translateX( -3150px ); }
    100% { transform: translateX( 0px );     }
}

@keyframes randomizer {
    100% {
        -webkit-transform: translateX( -50% );
                transform: translateX( -50% );
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">

    <div id="images">
        <div>
            <img src="https://demo.goodlayers.com/traveltour/citytour/wp-content/uploads/2017/07/shutterstock_14717125-700x500.jpg" />
            <img src="http://www.jomkembara.com/wp-content/uploads/2016/06/shutterstock_178807262-700x500.jpg" />
            <img src="https://inalentejo.com/wp-content/uploads/2017/01/almendres2-700x500.jpg" />
            <img src="http://www.narayanitours.com/wp-content/uploads/2017/07/delhi-mathura-jaipur-golden-trinagle-700x500.jpg" />
            <img src="http://files.fobby.net/0000/4e60/ultimatechimera.png" />
            <img src="https://i1.wp.com/www.travellingmachupicchu.com/wp-content/uploads/2017/06/machupicchu16-travelling-machu-picchu-1800x844px.jpg?resize=700%2C500&ssl=1" />
            <img src="http://indiatravel.nl/wp-content/uploads/2017/11/tiger-in-india-700x500.jpeg" />
            <img src="http://peruandinatours.com/wp-content/uploads/2017/07/aramu-muro-1-700x500.jpg" />
            <img src="http://files.fobby.net/0000/8930/halloween02.jpg" />
            <img src="https://www.tourwithtony.com/wp-content/uploads/2017/07/golden-gate-700x500.jpg" />
        </div>
    </div>

    <div id="randomizer">
    
        <div class="wheel">
            <div>
                <img src="https://i.imgur.com/ikch8mX.jpg" />
            </div>
        </div>
      
    </div>

</div>

Now some explanations

First of all, all images must have the same dimensions, or at least the same width, since we're manipulating the horizontal axis.

Here I've expanded your keyframes definition by adding a stopping point every 10%, which is 100 keyframes divided by 10 images, plus the starting point.

The trick here is to translate the X-axis by the width of the images, negatively and incrementally.

Considering the images used, randomly picked from Google Images Search, it's 0 (zero) for the starting point (first image) then -700px to show the second the image, -1400px for the second and so on until the last stop (100%) in which we set zero again to return to the first image.

In the demo below, however, I've incremented each step by 350px, 50% of the images' width because I was coding here, in this tiny little space >.<

Make sure to reflect these dimensions in the images' container — #images div

And then comes the speed part. Instead of using CSS variables I used animation-iteration-count through which I could control how many times the animation would run. This with a very small animation-duration resulted in a pretty decent image randomizer.

You can do what you were doing to simulate the tap strength. In fact, I did that, but since the range is much smaller than the one used for milliseconds generation, it was kind of ugly so I preferred to put that out.

The only thing you might need to do is adjust the same properties for the spinning wheel so they match visually, so when one stops the other stops together.

Hope that helps :)

Bruno Augusto
  • 1,455
  • 2
  • 14
  • 39