Спеціальний ефект, який імітує 3d колесо з Swiper 5


9

Мені потрібно скласти карусель з 12 предметів, які імітують 3d колесо, що обертається нескінченно. Щоб було зрозуміло, мені потрібно створити саме цей ефект:

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",
...

Як розробити власний ефект на зразок 3d-колеса, яке мені потрібно?


Цікаво, чи найкращим корисним способом буде робота з цим ефектом як відправною точкою: swiperjs.com/demos/240-effect-coverflow.html . Мені цікаво перенести "минулі слайди" на мінус осі x, хоча повернутися назад у праву частину повзунка для реінтродукції до шоу ...
Флума

1
@Phlume Вже намагався працювати з coverflowEffectпочатковою точкою і "хакерствує" її параметри, але це просто обхідне рішення, і я не можу отримати ефект першого кодексу. Слайди просто не розміщуються на круговій поверхні.
Фред К

Вибачте, ви можете уточнити, що ви хотіли б зробити? Ви хочете, щоб карусель можна було закручувати, не натискаючи кнопки попереднього / наступного?
Mukyuu

1
@Mukyuu Оновлено запитання з подробицями
Фред К

Відповіді:


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
е .. я провела пальцем зліва направо і колесо обертається вліво .... хоч виглядає круто
Цхаллачка

2
Тим часом дуже вдячний за вашу відповідь, але він не відповідає на багато вимог: 1) за твердженням @Tschallacka він обертається в зворотному напрямку. 2) слайди не слідують за пальцем, слайди повинні переміщати пальцем під час пальців (як це робить Swiper). 3) Швидким пальцем слід прокручувати безліч слайдів з імпульсом (як це робить Swiper). 4) Потім, коли колесо перестає обертатися, воно переходить до слайда (як це робить Swiper). 5) Мені потрібен зворотний дзвінок на подію slideChanged(як це робить Swiper). З усіх цих причин я вважав, що Swiper - це хороша відправна точка ...
Фред К

Помічено. Я змінив обертання в зворотному напрямку і додав деякий імпульс, спробую побачити, що я міг придумати Swiperjs. Скажіть, чи потрібно щось покращити.
Mukyuu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.