Niestandardowy efekt symulujący koło 3d za pomocą Swiper 5

9

Muszę zbudować karuzelę z 12 przedmiotami, które symulują nieskończenie obracające się koło 3d. Żeby było jasne, muszę stworzyć właśnie ten efekt:

https://codepen.io/SitePoint/pen/yXWXaw (znaleziono tutaj )

ale dzięki tym dodatkowym funkcjom (szczególnie na komputerach i urządzeniach mobilnych):

  1. slajdy muszą podążać krok po kroku za machnięciem, to znaczy slajdy powinny się przesuwać podczas przeciągania (podobnie jak Swiper).
  2. Szybkim przesunięciem powinno przewijać wiele slajdów z impetem (podobnie jak Swiper freeScroll).
  3. Następnie, gdy koło przestanie się obracać, zatrzaskuje się na przednim wózku (tak jak robi to Swiper freeModeStickyi centeredSlides), że jest to wybrany przez użytkownika.
  4. Potrzebuję oddzwonienia za każdym razem, gdy zmiana slajdu (jak zdarzenie slideChanged) (podobnie jak Swiper).

Z tych wszystkich powodów pomyślałem, że Swiper 5.3.0 będzie dobrym punktem wyjścia.

Próbowałem różnych obejść, tym lepszym jest ta konfiguracja, ale loop: truejest to okropne obejście i powoduje problemy (sprawdź komentarze):

  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,

  });

Zdecydowanie niewłaściwa droga.

Myślę, że właściwą drogą jest opracowanie zwyczaj Swiper effect(jak wbudowany cubeEffect, coverflowEffect...), który symuluje koło bez użycia loop:trueże kwestie przyczyn. Na przykład tutaj facet tworzy swój własny efekt, który następnie ustawia w effectatrybucie Swiper: https://codepen.io/paralleluniv3rse/pen/yGQjMv

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

Jak stworzyć niestandardowy efekt, taki jak koło 3D, którego potrzebuję?

Fred K.
źródło
Zastanawiam się, czy praca z tym efektem jako punktem wyjścia byłaby najlepszym korzystnym sposobem: swiperjs.com/demos/240-effect-coverflow.html . Jestem ciekawy przesunięcia „poprzednich slajdów” na ujemną oś x, aby wrócić z powrotem do prawej strony suwaka i odtworzyć na pokaz ...
Phlume,
1
@Phlume Już próbowałem pracować z coverflowEffectpunktem początkowym i „hackować” jego parametry, ale to tylko obejście i nie mogę uzyskać efektu pierwszego codepen. Zjeżdżalnie po prostu nie będą umieszczone na okrągłej powierzchni.
Fred K
Przepraszam, czy możesz wyjaśnić, co chcesz zrobić? Czy chcesz obracać karuzelę bez klikania przycisków poprzedni / następny?
Mukyuu
1
@Mukyuu Zaktualizowano wpis pytania ze szczegółami
Fred K

Odpowiedzi:

2

Myślę, że tego właśnie chcesz: https://codepen.io/mukyuu/pen/GRgPYqG .

Prawie spełnił twoje warunki, z wyjątkiem tego, że nie używa Swipera 5 i przystawki.

  1. Obraca się zgodnie z kierunkiem machnięcia.
  2. Po szybkim przesunięciu powinien przewijać wiele slajdów z impetem (podobnie jak Swiper).
  3. Następnie, gdy koło przestanie się obracać, zatrzaskuje się (tak jak Swiper).
  4. W ontouchfunkcji występuje wywołanie zwrotne.

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)

Testowany w przeglądarkach na Androida 9 i Windows 10.

Mukyuu
źródło
3
uhhh .. Przesuwam od lewej do prawej, a koło obraca się w lewo .... choć wygląda fajnie
Tschallacka
2
Tymczasem bardzo dziękuję za odpowiedź, ale nie odpowiada na wiele wymagań: 1) jak stwierdził @Tschallacka, obraca się w odwrotnym kierunku. 2) slajdy nie podążają za przeciągnięciem, slajdy powinny przesuwać przeciągnięcie podczas przeciągania (podobnie jak Swiper). 3) Szybkie przeciągnięcie powinno przewijać wiele slajdów z impetem (podobnie jak Swiper). 4) Następnie, gdy koło przestanie się obracać, zatrzaskuje się (tak jak Swiper). 5) Potrzebuję oddzwonienia w przypadku zdarzenia takiego jak slideChanged( podobnie jak Swiper). Z tych wszystkich powodów myślałem, że Swiper będzie dobrym punktem wyjścia ...
Fred K
Odnotowany. Zmodyfikowałem rotacje w odwrotnej kolejności i dodaję pędu, postaram się zobaczyć, co mógłbym wymyślić Swiperjs. Powiedz mi, jeśli coś wymaga dalszej poprawy.
Mukyuu