Efek khusus yang mensimulasikan roda 3d dengan Swiper 5

9

Saya perlu membuat korsel dengan 12 item yang mensimulasikan roda 3d berputar tanpa batas. Agar jelas, saya harus membuat efek ini:

https://codepen.io/SitePoint/pen/yXWXaw (ditemukan di sini )

tetapi dengan fitur-fitur tambahan ini (terutama pada desktop dan ponsel):

  1. slide harus mengikuti langkah-demi-langkah gesekan, yaitu slide harus bergerak sambil menggesek (seperti yang dilakukan Swiper).
  2. Dengan gesek cepat, ia akan menggulir banyak slide dengan momentum (seperti yang dilakukan Swiper freeScroll).
  3. Kemudian ketika roda berhenti berputar itu terkunci ke slide depan (seperti yang dilakukan Swiper freeModeStickydan centeredSlides) bahwa itu yang dipilih dari pengguna.
  4. Saya perlu panggilan balik setiap kali perubahan slide (seperti acara slideChanged) (seperti yang dilakukan Swiper).

Untuk semua alasan ini saya pikir Swiper 5.3.0 akan menjadi titik awal yang baik.

Saya mencoba berbagai solusi, yang lebih baik adalah dengan pengaturan ini, tetapi loop: truesolusi yang buruk dan menyebabkan masalah (periksa komentar):

  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,

  });

Jelas bukan jalan yang benar.

Saya pikir cara yang benar adalah untuk mengembangkan kebiasaan Swiper effect(seperti built-in cubeEffect, coverflowEffect, ...) yang mensimulasikan roda, tanpa menggunakan loop:truebahwa isu-isu penyebab. Sebagai contoh, di sini seorang pria menciptakan efek kustomnya sendiri yang kemudian ia atur dalam effectatribut Swiper: https://codepen.io/paralleluniv3rse/pen/yGQjMv

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

Bagaimana cara mengembangkan efek khusus seperti roda 3d yang saya butuhkan?

Fred K
sumber
Saya ingin tahu apakah bekerja dengan efek ini sebagai titik awal akan menjadi cara terbaik yang menguntungkan: swiperjs.com/demos/240-effect-coverflow.html . Saya ingin tahu tentang memindahkan "slide terakhir" pada sumbu x negatif meskipun untuk kembali ke sisi kanan slider untuk reitroduksi ke pertunjukan ...
Phlume
1
@ Phlume Sudah mencoba untuk bekerja dengan coverflowEffectsebagai titik awal dan "meretas" parameternya tetapi itu hanya solusi, dan saya tidak bisa mendapatkan efek codepen pertama. Slide tidak akan ditempatkan pada permukaan lingkaran.
Fred K
Maaf, bisakah Anda mengklarifikasi apa yang ingin Anda lakukan? Apakah Anda ingin korsel menjadi spinnable tanpa mengklik tombol prev / next?
Mukyuu
1
@Mukyuu Posting pertanyaan yang diperbarui dengan detail
Fred K

Jawaban:

2

Saya pikir inilah yang Anda inginkan: https://codepen.io/mukyuu/pen/GRgPYqG .

Hampir memenuhi persyaratan Anda kecuali untuk itu tidak menggunakan Swiper 5 dan jepret.

  1. Ini berputar dengan arah gesek.
  2. Dengan gesekan cepat, itu akan menggulir banyak slide dengan momentum (seperti yang dilakukan Swiper).
  3. Kemudian ketika roda berhenti berputar itu terkunci ke slide (seperti yang dilakukan Swiper).
  4. Dalam ontouchfungsinya ada panggilan balik.

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)

Diuji di browser Android 9 dan Windows 10.

Mukyuu
sumber
3
uhhh .. Saya menggesek dari kiri ke kanan dan roda berputar ke kiri .... terlihat keren
Tschallacka
2
Sementara itu, sangat terima kasih atas jawaban Anda, tetapi tidak menanggapi banyak persyaratan: 1) seperti yang dinyatakan oleh @Tschallacka berputar secara terbalik. 2) slide tidak mengikuti swipe, slide harus menggerakkan swipe sambil menggesek (seperti yang dilakukan Swiper). 3) Dengan gesekan cepat, ia akan menggulir banyak slide dengan momentum (seperti yang dilakukan Swiper). 4) Kemudian ketika roda berhenti berputar itu terkunci ke slide (seperti yang dilakukan Swiper). 5) Saya perlu panggilan balik pada acara seperti slideChanged(seperti yang dilakukan Swiper). Untuk semua alasan ini saya pikir Swiper akan menjadi titik awal yang baik ...
Fred K
Dicatat. Saya telah memodifikasi rotasi secara terbalik dan menambahkan momentum, saya akan mencoba melihat apa yang bisa saya hasilkan dengan Swiperjs. Katakan jika ada yang perlu diperbaiki.
Mukyuu