Красивая 3D карусель, которая управляется зажатием и движением мышки


Возможности карусели:

  • Любое количество фотографий.
  • Произвольный радиус.
  • Автоматическое движение с заданной скоростью.
  • Отсутствие дополнительных библиотек.
  • Всего 90 строк кода.

ДЕМО - ПРИМЕР:
HTML КОД:

<div id="carousel">
    <div id="drag-container">
        <div id="spin-container">
            <a href="/photo-large.jpg"><img src="/photo-small.jpg" alt="" /></a>
            <!-- Еще фото -->
            <a href="/photo-large.jpg"><img src="/photo-small.jpg" alt="" /></a>
            <p>3D Carousel</p>
        </div>
        <div id="ground"></div>
    </div>
</div>

CSS КОД:

#carousel  {
    touch-action: none;
    display: flex;
    height: 400px;
    perspective: 1000px;
    transform-style: preserve-3d;
}
#drag-container,
#spin-container {
    position: relative;
    display: flex;
    margin: auto;
    transform-style: preserve-3d;
    transform: rotateX(-10deg);
}
#drag-container a {
    transform-style: preserve-3d;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    box-shadow: 0 4px 8px rgba(0,0,0,0.2), 0 10px 16px rgba(0,0,0,0.1);
    border: 8px solid #BFE2FF;
}
#drag-container img {
    width: 100%;
    height: 100%;
    filter: grayscale(50%) contrast(0.8);
    transition: filter 0.3s;
}     
#drag-container a:hover img{
    filter: grayscale(0) contrast(1);
}          
#drag-container p {
    line-height: 1;
    font-size: 50px;
    font-weight: bold;
    text-align: center;      
    font-family: Serif;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translate(-50%, -50%) rotateX(90deg);
    color: #337AB7;
    font-family: Verdana, sans-serif; 
}
#ground {
    width: 900px;
    height: 900px;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translate(-50%, -50%) rotateX(90deg);
}
@keyframes spin {
    from {
        transform: rotateY(0deg);
    }
    to {
        transform: rotateY(360deg);
    }
}
@keyframes spinRevert {
    from {
        transform: rotateY(360deg);
    }
    to {
        transform: rotateY(0deg);
    }
}    

JAVASCRIPT КОД:

let radius = 240; 
let autoRotate = true;
let rotateSpeed = -60; 
let imgWidth = 140; 
let imgHeight = 205;
setTimeout(init, 300);
let odrag = document.getElementById("drag-container");
let ospin = document.getElementById("spin-container");
let carousel = document.getElementById("carousel");
let aImg = ospin.getElementsByTagName("a");
ospin.style.width = imgWidth + "px";
ospin.style.height = imgHeight + "px";
let ground = document.getElementById("ground");
ground.style.width = radius * 3 + "px";
ground.style.height = radius * 3 + "px";
function init(delayTime) {
    for (let i = 0; i < aImg.length; i++) {
        aImg[i].style.transform =
        "rotateY(" +
        i * (360 / aImg.length) +
        "deg) translateZ(" +
        radius +
        "px)";
        aImg[i].style.transition = "transform 1s";
        aImg[i].style.transitionDelay =
        delayTime || (aImg.length - i) / 4 + "s";
    }
}
function applyTranform(obj) {
    if (tY > 180) tY = 180;
    if (tY < 0) tY = 0;
    obj.style.transform = "rotateX(" + -tY + "deg) rotateY(" + tX + "deg)";
}
function playSpin(yes) {
    ospin.style.animationPlayState = yes ? "running" : "paused";
}
let sX,
sY,
nX,
nY,
desX = 0,
desY = 0,
tX = 0,
tY = 10;
if (autoRotate) {
    let animationName = rotateSpeed > 0 ? "spin" : "spinRevert";
    ospin.style.animation = `${animationName} ${Math.abs(
    rotateSpeed
    )}s infinite linear`;
}
carousel.onpointerdown = function(e) {
    clearInterval(odrag.timer);
    e = e || window.event;
    let sX = e.clientX,
    sY = e.clientY;
    this.onpointermove = function(e) {
        e = e || window.event;
        let nX = e.clientX,
        nY = e.clientY;
        desX = nX - sX;
        desY = nY - sY;
        tX += desX * 0.1;
        tY += desY * 0.1;
        applyTranform(odrag);
        sX = nX;
        sY = nY;
    };
    this.onpointerup = function(e) {
        odrag.timer = setInterval(function() {
            desX *= 0.95;
            desY *= 0.95;
            tX += desX * 0.1;
            tY += desY * 0.1;
            applyTranform(odrag);
            playSpin(false);
            if (Math.abs(desX) < 0.5 && Math.abs(desY) < 0.5) {
                clearInterval(odrag.timer);
                playSpin(true);
            }
        }, 17);
        this.onpointermove = this.onpointerup = null;
    };
    return false;
};

Где:

  • radius - Радиус карусели
  • autoRotate - Автоматическое вращение (true / false)
  • rotateSpeed - Скорость вращения
  • imgWidth - Ширина фотографии
  • imgHeight - Высота фотографии

Чтобы карусель вращалась зажатием и движением мыши в любом месте документа, а не только в ее контейнере, нужно заменить carousel.onpointerdown = function(e) {...} на  document.onpointerdown = function(e) {...}

Если в карусели не используются ссылки, а только фотографии, то:

  • В HTML убираем ссылки
  • В CSS удаляем класс #drag-container img и заменяем #drag-container a на #drag-container img
  • В JS заменяем let aImg = ospin.getElementsByTagName("a"); на let aImg = ospin.getElementsByTagName("img");

У автора сделано одновременно оба варианта (с ссылкой и без), но в этом случае происходит конфликт в Firefox. Видео, которое также присутствует в оригинале, я удалил за ненадобностью, как и фоновую музыку.

Если нужно изменять радиус карусели колесом мыши, добавьте вниз скрипта:

JAVASCRIPT КОД:

document.onmousewheel = function(e) {
    e = e || window.event;
    let d = e.wheelDelta / 20 || -e.detail;
    radius += d;
    init(1);
};