ผลที่กำหนดเองที่จำลองล้อ 3 มิติด้วย Swiper 5


9

ฉันจำเป็นต้องสร้างม้าหมุนที่มี 12 รายการที่จำลองล้อหมุน 3 มิติอย่างไม่สิ้นสุด เพื่อความชัดเจนฉันต้องสร้างเอฟเฟกต์นี้อย่างแม่นยำ:

https://codepen.io/SitePoint/pen/yXWXaw (ดูที่นี่ )

แต่ด้วยคุณสมบัติการเพิ่มเหล่านี้ (บนเดสก์ท็อปและอุปกรณ์เคลื่อนที่โดยเฉพาะ):

  1. สไลด์ต้องทำตามการกวาดนิ้วทีละขั้นตอนนั่นคือสไลด์ควรเลื่อนในขณะที่เลื่อน (ตามที่ Swiper ทำ)
  2. ด้วยการรูดอย่างรวดเร็วก็ควรเลื่อนภาพนิ่งจำนวนมากที่มีโมเมนตัม (ตามที่ Swiper จะมี freeScroll)
  3. จากนั้นเมื่อล้อหยุดหมุนมันจะเลื่อนไปที่สไลด์ด้านหน้า (อย่างที่ Swiper ทำด้วยfreeModeStickyและ centeredSlides) ว่าเป็นล้อที่ถูกเลือกจากผู้ใช้
  4. ฉันต้องการโทรกลับทุกครั้งที่มีการเปลี่ยนแปลงสไลด์ (เช่นเหตุการณ์slideChanged) (ตามที่ Swiper ทำ)

ด้วยเหตุผลทั้งหมดนี้ฉันจึงคิดว่าSwiper 5.3.0จะเป็นจุดเริ่มต้นที่ดี

ฉันลองวิธีแก้ปัญหาต่าง ๆ ที่ดีกว่าคือการตั้งค่านี้ แต่loop: trueเป็นวิธีแก้ปัญหาอันยิ่งใหญ่และทำให้เกิดปัญหา (ตรวจสอบความคิดเห็น):

  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,

  });

ไม่ใช่วิธีที่ถูกต้องแน่นอน

ผมคิดว่าวิธีการที่เหมาะสมคือการพัฒนาที่กำหนดเอง Swiper effect(เช่นในตัวcubeEffect, coverflowEffect, ... ) ที่เลียนแบบล้อโดยไม่ต้องใช้loop:trueว่าปัญหาสาเหตุ ตัวอย่างเช่นที่นี่ผู้ชายสร้างเอฟเฟ็กต์แบบกำหนดเองของเขาเองจากนั้นเขาตั้งค่าในeffectแอตทริบิวต์ของ Swiper: https://codepen.io/paralleluniv3rse/pen/yGQjMv

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

วิธีพัฒนาเอฟเฟกต์แบบกำหนดเองเช่นล้อ 3 มิติที่ฉันต้องการ


ฉันสงสัยว่าการทำงานร่วมกับผลกระทบนี้เป็นจุดเริ่มต้นที่จะเป็นวิธีที่ดีที่สุดที่เป็นประโยชน์: swiperjs.com/demos/240-effect-coverflow.html ฉันอยากรู้เกี่ยวกับการย้าย "ภาพนิ่งที่ผ่านมา" บนแกน x เชิงลบแม้ว่าจะย้อนกลับไปทางด้านขวาของแถบเลื่อนเพื่อนำกลับมาแสดงอีกครั้ง ...
Phlume

1
@Phlume พยายามทำงานด้วยcoverflowEffectเป็นจุดเริ่มต้นและ "แฮ็ค" พารามิเตอร์ แต่มันก็เป็นเพียงวิธีการแก้ปัญหาและฉันไม่สามารถรับผลของ codepen แรกได้ สไลด์จะไม่วางบนพื้นผิววงกลม
Fred K

ขออภัยคุณสามารถชี้แจงสิ่งที่คุณต้องการจะทำอย่างไร คุณต้องการหมุนแบบหมุนได้โดยไม่ต้องคลิกปุ่มก่อนหน้า / ถัดไปหรือไม่?
Mukyuu

1
@Mukyuu อัปเดตโพสต์คำถามพร้อมรายละเอียด
Fred K

คำตอบ:


2

ผมคิดว่านี่คือสิ่งที่คุณต้องการ: https://codepen.io/mukyuu/pen/GRgPYqG

เกือบจะเป็นไปตามเงื่อนไขของคุณยกเว้นว่าไม่ได้ใช้ Swiper 5 และ snap

  1. มันหมุนไปตามทิศทางของการปัด
  2. ด้วยการปัดอย่างรวดเร็วมันควรเลื่อนสไลด์จำนวนมากด้วยโมเมนตัม (เช่นเดียวกับ Swiper)
  3. จากนั้นเมื่อล้อหยุดหมุนมันจะเลื่อนไปที่สไลด์ (เช่นเดียวกับ Swiper)
  4. ในontouchฟังก์ชั่นมีการเรียกกลับ

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)

ทดสอบในเบราว์เซอร์ Android 9 และ Windows 10


3
uhhh .. ฉันปัดจากซ้ายไปขวาและล้อหมุนไปทางซ้าย .... ดูเจ๋งเลยนะ
Tschallacka

2
ในขณะเดียวกันขอบคุณมากสำหรับคำตอบของคุณ แต่ไม่ตอบสนองต่อความต้องการมากมาย: 1)ตามที่ @Tschallacka หมุนไปในทางตรงกันข้าม 2)สไลด์ไม่เป็นไปตามการกวาดนิ้วสไลด์ควรเลื่อนการปัดในขณะที่ปัด (ตามที่ Swiper ทำ) 3)ด้วยการปัดเร็วมันควรเลื่อนหลายสไลด์ด้วยโมเมนตัม (เช่นเดียวกับ Swiper) 4)จากนั้นเมื่อล้อหยุดหมุนมันจะเลื่อนไปที่สไลด์ (เช่นเดียวกับ Swiper) 5)ฉันต้องการการติดต่อกลับในเหตุการณ์เช่นslideChanged(เช่นเดียวกับ Swiper) ด้วยเหตุผลทั้งหมดนี้ฉันคิดว่า Swiper น่าจะเป็นจุดเริ่มต้นที่ดี ...
Fred K

ข้อสังเกต. ฉันได้แก้ไขการหมุนแบบย้อนกลับและเพิ่มโมเมนตัมบางอย่างฉันจะพยายามดูว่าฉันจะเกิดอะไรขึ้นกับ Swiperjs บอกฉันถ้ามีอะไรต้องการการปรับปรุงเพิ่มเติม
Mukyuu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.