Hiệu ứng tùy chỉnh mô phỏng bánh xe 3d với Swiper 5


9

Tôi cần xây dựng một băng chuyền với 12 vật phẩm mô phỏng bánh xe 3d quay vô hạn. Để rõ ràng, tôi cần tạo chính xác hiệu ứng này:

https://codepen.io/SitePoint/pen/yXWXaw (tìm thấy ở đây )

nhưng với các tính năng bổ sung này (đặc biệt trên máy tính để bàn và thiết bị di động):

  1. các slide phải theo từng bước vuốt, đó là các slide nên di chuyển trong khi vuốt (như Swiper làm).
  2. Với thao tác vuốt nhanh, nó sẽ cuộn nhiều slide với đà (như Swiper làm với freeScroll).
  3. Sau đó, khi bánh xe dừng quay, nó sẽ trượt vào slide trước (như Swiper làm với freeModeStickycenteredSlides) đó là cái được chọn từ người dùng.
  4. Tôi cần một cuộc gọi lại mỗi lần thay đổi slide (như sự kiện slideChanged) (như Swiper làm).

Vì tất cả những lý do này, tôi nghĩ Swiper 5.3.0 sẽ là điểm khởi đầu tốt.

Tôi đã thử nhiều cách giải quyết khác nhau, cách tốt hơn là với thiết lập này, nhưng đó loop: truelà một cách giải quyết khủng khiếp và gây ra sự cố (kiểm tra các bình luận):

  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,

  });

Chắc chắn không phải là cách đúng đắn.

Tôi nghĩ một cách đúng đắn là phát triển một Swiper tùy chỉnh effect(như được xây dựng trong cubeEffect, coverflowEffect...) mà mô phỏng bánh xe, mà không sử dụng loop:truerằng các vấn đề nguyên nhân. Ví dụ: ở đây, một anh chàng tạo hiệu ứng tùy chỉnh của riêng mình mà sau đó anh ta đặt trong effectthuộc tính của Swiper: https://codepen.io/abuluniv3rse/pen/yGQjMv

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

Làm cách nào để phát triển hiệu ứng tùy chỉnh như bánh xe 3d tôi cần?


Tôi tự hỏi nếu làm việc với hiệu ứng này như một điểm khởi đầu sẽ là cách có lợi nhất: swiperjs.com/demos/240-effect-coverflow.html . Tôi tò mò về việc di chuyển "các slide trong quá khứ" trên trục x âm mặc dù để quay lại phía bên phải của thanh trượt để giới thiệu lại cho chương trình ...
Phlume

1
@Phlume Đã cố gắng làm việc với coverflowEffecttư cách là điểm khởi đầu và "hack" các tham số của nó nhưng đó chỉ là một cách giải quyết và tôi không thể có được hiệu ứng của codepen đầu tiên. Các slide chỉ đơn giản là không được đặt trên một bề mặt tròn.
Fred K

Xin lỗi, bạn có thể làm rõ những gì bạn muốn làm? Bạn có muốn băng chuyền có thể phát âm mà không cần nhấp vào nút trước / tiếp theo không?
Mukyuu

1
@Mukyuu Cập nhật câu hỏi với chi tiết
Fred K

Câu trả lời:


2

Tôi nghĩ rằng đây là những gì bạn muốn: https://codepen.io/mukyuu/pen/GRgPYqG .

Nó gần như đáp ứng các điều kiện của bạn ngoại trừ việc nó không sử dụng Swiper 5 và snap.

  1. Nó đang quay với hướng vuốt.
  2. Với thao tác vuốt nhanh, nó sẽ cuộn nhiều slide với đà (như Swiper làm).
  3. Sau đó, khi bánh xe dừng quay, nó khớp với một slide (như Swiper làm).
  4. Trong ontouchchức năng có một cuộc gọi lại.

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)

Đã thử nghiệm trong trình duyệt Android 9 và Windows 10.


3
uhhh .. Tôi vuốt từ trái sang phải và bánh xe quay sang trái .... mặc dù trông rất tuyệt
Tschallacka

2
Trong khi đó rất cảm ơn câu trả lời của bạn, nhưng nó không đáp ứng với rất nhiều yêu cầu: 1) như đã nêu bởi @Tschallacka, nó quay ngược lại. 2) các trang chiếu không tuân theo thao tác vuốt, các trang trình bày sẽ di chuyển thao tác vuốt trong khi vuốt (như Swiper thực hiện). 3) Với thao tác vuốt nhanh, nó sẽ cuộn nhiều slide với động lượng (như Swiper làm). 4) Sau đó, khi bánh xe dừng quay, nó sẽ trượt vào một slide (như Swiper làm). 5) Tôi cần một cuộc gọi lại về sự kiện như slideChanged(như Swiper làm). Vì tất cả những lý do này, tôi nghĩ Swiper sẽ là điểm khởi đầu tốt ...
Fred K

Lưu ý Tôi đã sửa đổi các góc quay ngược lại và thêm một số động lượng, tôi sẽ thử xem những gì tôi có thể đến với Swiperjs. Hãy cho tôi biết nếu bất cứ điều gì cần cải thiện nhiều hơn.
Mukyuu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.