Effetto personalizzato che simula una ruota 3d con Swiper 5


9

Devo costruire una giostra con 12 elementi che simulano una ruota 3d che gira all'infinito. Per essere chiari, devo creare esattamente questo effetto:

https://codepen.io/SitePoint/pen/yXWXaw (disponibile qui )

ma con queste funzionalità aggiuntive (specialmente su desktop e dispositivi mobili):

  1. le diapositive devono seguire passo-passo il passaggio, ovvero le diapositive devono spostarsi mentre si scorre (come fa Swiper).
  2. Con uno scorrimento veloce dovrebbe scorrere molte diapositive con slancio (come fa Swiper freeScroll).
  3. Quindi quando la ruota smette di ruotare si aggancia alla slitta anteriore (come Swiper fa con freeModeStickye centeredSlides) che è quella scelta dall'utente.
  4. Ho bisogno di una richiamata ogni volta che cambia una diapositiva (come un evento slideChanged) (come fa Swiper).

Per tutti questi motivi ho pensato che Swiper 5.3.0 sarebbe stato un buon punto di partenza.

Ho provato varie soluzioni alternative, la migliore è con questa configurazione, ma loop: trueè una soluzione terribile e causa problemi (controlla i commenti):

  var swiper = new Swiper(el_class, {
    slidesPerView: 1.5,
    spaceBetween: 25,
    centeredSlides: true,
    grabCursor: true,
    speed: 550,
    loop: true, // <== repeat infinitely the 12 items. with fast scroll at the end of a cycle it waits a while before render the next cycle. Awful
    loopAdditionalSlides: 10, 

    // Free mode
    freeMode: true, // <== free scrolling. Good
    freeModeMomentumRatio: 1,
    freeModeMomentumVelocityRatio: 1.5,
    freeModeMomentumBounceRatio: 1,
    freeModeMinimumVelocity: 0.02,
    freeModeSticky: true, // <== snap to the slides. Good

    // Touch Resistance
    resistanceRatio: 0.85,

    // Prevent blurry texts
    roundLengths: true,

  });

Sicuramente non nel modo giusto.

Penso che il modo giusto è quello di sviluppare un costume Swiper effect(come il built-in cubeEffect, coverflowEffect, ...) che simula la ruota, senza l'utilizzo loop:trueche i problemi di cause. Ad esempio, qui un ragazzo crea il proprio effetto personalizzato che imposta effectnell'attributo di Swiper: https://codepen.io/paralleluniv3rse/pen/yGQjMv

...
effect: "myCustomTransition",
...

Come sviluppare un effetto personalizzato come la ruota 3d di cui ho bisogno?


Mi chiedo se lavorare con questo effetto come punto di partenza sarebbe il miglior modo benefico: swiperjs.com/demos/240-effect-coverflow.html . Sono curioso di spostare le "diapositive passate" su un asse x negativo, anche se per tornare al lato destro del cursore per la reintroduzione allo spettacolo ...
Phlume

1
@Phlume Ho già provato a lavorare coverflowEffectcome punto di partenza e "hackerando" i suoi parametri, ma è solo una soluzione alternativa e non riesco a ottenere l'effetto del primo codepen. Le diapositive semplicemente non verranno posizionate su una superficie circolare.
Fred K,

Siamo spiacenti, puoi chiarire cosa ti piacerebbe fare? Vuoi che la giostra sia spinnabile senza fare clic sui pulsanti precedente / successivo?
Mukyuu, il

1
@Mukyuu Post di domanda aggiornato con dettagli
Fred K

Risposte:


2

Penso che questo sia quello che vuoi: https://codepen.io/mukyuu/pen/GRgPYqG .

Ha quasi soddisfatto le tue condizioni, tranne per il fatto che non utilizza Swiper 5 e Snap.

  1. Sta ruotando con la direzione del colpo.
  2. Con uno scorrimento veloce, dovrebbe scorrere molte diapositive con slancio (come Swiper).
  3. Quindi, quando la ruota smette di ruotare, scatta su una diapositiva (come fa Swiper).
  4. In ontouchfunzione c'è un callback.

HTML:

<div class="carousel" id="wrapper">
    <figure>
    <img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
    <img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
        <img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
        <img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
        <img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
        <img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
    <img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
        <img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
    </figure>
</div>

S (CSS):

body {
    margin: 0;
    font-family: 'Roboto';
    font-size: 16px;

    display: flex;
    flex-direction: column;
    height: 100vh;
    justify-content: center;
}

// Carousel configuration parameters
$n: 8;
$item-width: 400px;
$item-separation: 80px;
$viewer-distance: 500px;

// Derived variables
$theta: 2 * 3.141592653589793 / $n; 
$apothem: 482.842712474619px;

.carousel {
    padding: 20px;

    perspective: $viewer-distance;
    overflow: hidden;

    display: flex;
    flex-direction: column;
    align-items: center;
    > * {
        flex: 0 0 auto;
    }

    figure {
        cursor: grab;
        margin: 0;

        width: $item-width;
        transform-style: preserve-3d;
        transition: transform 0.5s;
        transform-origin: 50% 50% (-$apothem);

        img {
            width: 100%;
            box-sizing: border-box;
            padding: 0 $item-separation / 2;

            opacity: 0.9;

            &:not(:first-of-type) {
                position: absolute;
                left: 0;
                top: 0;
                transform-origin: 50% 50% (-$apothem);
            }

            @for $i from 2 through $n {
                &:nth-child(#{$i}) {
                    transform: rotateY(#{($i - 1) * $theta}rad);
                }
            }
        }
    }

    nav {
        display: flex;
        justify-content: center;
        margin: 20px 0 0;

        button {
            flex: 0 0 auto;
            margin: 0 5px;

            cursor: pointer;

            color: #333;
            background: none;
            border: 1px solid;
            letter-spacing: 1px;
            padding: 5px 10px;
        }
    }
}

JS:

var
    carousel = document.querySelector('.carousel'),
    figure = carousel.querySelector('figure'),
    nav = carousel.querySelector('nav'),
    numImages = figure.childElementCount,
    theta =  2 * Math.PI / numImages,
    currImage = 0
;

// add touch detect:
function ontouch(el, callback){
 // Modified from http://www.javascriptkit.com/javatutors/touchevents3.shtml
    var touchsurface = el,
    dir,
    swipeType,
    startX,
    startY,
    distX,
    distY,
    threshold = 150, //required min distance traveled to be considered swipe
    restraint = 100, // maximum distance allowed at the same time in perpendicular direction
    allowedTime = 500, // maximum time allowed to travel that distance
    elapsedTime,
    startTime,
    handletouch = callback || function(evt, dir, phase, swipetype, distance){}

    touchsurface.addEventListener('touchstart', function(e){
        var touchobj = e.changedTouches[0]
        dir = 'none'
        swipeType = 'none'
        dist = 0
        startX = touchobj.pageX
        startY = touchobj.pageY
        startTime = new Date().getTime() // record time when finger first makes contact with surface
        handletouch(e, 'none', 'start', swipeType, 0) // fire callback function with params dir="none", phase="start", swipetype="none" etc
        e.preventDefault()

    }, false)

    touchsurface.addEventListener('touchmove', function(e){
        var touchobj = e.changedTouches[0]
        distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface
        distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface
        if (Math.abs(distX) > Math.abs(distY)){ // if distance traveled horizontally is greater than vertically, consider this a horizontal movement
            dir = (distX < 0)? 'left' : 'right'
            handletouch(e, dir, 'move', swipeType, distX) // fire callback function with params dir="left|right", phase="move", swipetype="none" etc
        }
        else{ // else consider this a vertical movement
            dir = (distY < 0)? 'up' : 'down'
            handletouch(e, dir, 'move', swipeType, distY) // fire callback function with params dir="up|down", phase="move", swipetype="none" etc
        }
        e.preventDefault() // prevent scrolling when inside DIV
    }, false)

    touchsurface.addEventListener('touchend', function(e){
        var touchobj = e.changedTouches[0]
        elapsedTime = new Date().getTime() - startTime // get time elapsed
        if (elapsedTime <= allowedTime){ // first condition for awipe met
            if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
                swipeType = dir // set swipeType to either "left" or "right"
            }
            else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
                swipeType = dir // set swipeType to either "top" or "down"
            }
        }
        // Fire callback function with params dir="left|right|up|down", phase="end", swipetype=dir etc:
        handletouch(e, dir, 'end', swipeType, (dir =='left' || dir =='right')? distX : distY)
        e.preventDefault()
    }, false)
}
function DoSomething(dir, distance) {
  //modifiy this function for wheel rotation (prev/next) images
  var momentum = 100; // modify this value for how much momentum expected to switch to next/prev images
  switch (dir){
    case 'left':
    case 'right':
      currImage+= Math.round(distance/momentum);
      break;
  }
    figure.style.transform = `rotateY(${currImage * -theta}rad)`;
}
document.getElementById('wrapper').ondragstart = function() { return false; }; // prevent image dragged on mouse drag
window.addEventListener('load', function() {
  var dir, phase, el = document.getElementById('wrapper'),
    position = {
      X: 0,
      Y: 0
    };

  el.onmousedown = function(down) {
    position.X = down.clientX;
    position.Y = down.clientY;
  };

  el.onmouseup = function(up) {
    distX = up.clientX - position.X; // get horizontal dist traveled by finger while in contact with surface
    distY = position.Y - up.clientY; // get vertical dist traveled by finger while in contact with surface
    if (Math.abs(distX) > Math.abs(distY)) { // if distance traveled horizontally is greater than vertically, consider this a horizontal movement
      dir = (distX < 0) ? 'left' : 'right';
      distance = distX;
    } else { // else consider this a vertical movement
      dir = (distY < 0) ? 'down' : 'up';
      distance = distY;
    }
    dir = (distance == 0) ? 'none' : dir;
    DoSomething(dir, distance); // simulate touch from mouse control
  }; 
  ontouch(el, function(evt, dir, phase, swipetype, distance){
 // evt: contains original Event object
 // dir: contains "none", "left", "right", "top", or "down"
 // phase: contains "start", "move", or "end"
 // swipetype: contains "none", "left", "right", "top", or "down"
 // distance: distance traveled either horizontally or vertically, depending on dir value

 if ( phase == 'end' && (dir =='left' || dir == 'right') ) // on succesful swipe
   DoSomething(dir, distance);
})
}, false)

Testato nei browser Android 9 e Windows 10.


3
uhhh .. scorro da sinistra a destra e la ruota gira a sinistra .... sembra bello però
Tschallacka

2
Nel frattempo, molto, grazie per la tua risposta, ma non risponde a molti requisiti: 1) come affermato da @Tschallacka ruota al contrario. 2) le diapositive non seguono lo scorrimento, le diapositive devono spostare lo scorrimento durante lo scorrimento (come fa Swiper). 3) Con uno scorrimento veloce dovrebbe scorrere molte diapositive con slancio (come Swiper). 4) Quindi, quando la ruota smette di ruotare, scatta su una slitta (come Swiper). 5) Ho bisogno di una richiamata su un evento come slideChanged(come fa Swiper). Per tutti questi motivi ho pensato che Swiper sarebbe stato un buon punto di partenza ...
Fred K,

Notato. Ho modificato le rotazioni al contrario e ho aggiunto un po 'di slancio, proverò a vedere cosa potrei inventare gli Swiperjs. Dimmi se qualcosa ha bisogno di più miglioramenti.
Mukyuu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.