<div class="tip">Use your finger to swipe it up and down :)</div>
<div class="swiper"></div>
<div class="camera">
<div class="d-carousel">
<!-- 图片可任意增减,无需调节其他参数 -->
<img src="https://picsum.photos/id/1/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/2/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/3/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/4/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/5/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/6/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/7/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/8/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/9/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/10/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/11/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/12/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/13/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/14/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/15/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/16/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/17/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/18/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/19/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/20/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/21/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/22/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/23/200/300" alt="" class="d-carousel-item">
<img src="https://picsum.photos/id/24/200/300" alt="" class="d-carousel-item">
</div>
</div>
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background: hsl(240, 56%, 98%);
margin: 0;
}
.swiper {
position: absolute;
width: 120px;
height: 468px;
cursor: pointer;
}
.camera {
$d-carousel-item-width: 45px;
$d-carousel-item-height: 45px;
--d-carousel-item-gap: 5px;
--d-carousel-transition: 0.3s;
--d-carousel-perspective: 250px;
--d-carousel-rotate-x: 0;
--d-carousel-translate-z: -100px;
--d-carousel-item-width: #{$d-carousel-item-width};
--d-carousel-item-height: #{$d-carousel-item-height};
--d-carousel-item-gap-double: calc(var(--d-carousel-item-gap) * 2);
--camera-width: calc(
var(--d-carousel-item-width) + var(--d-carousel-item-gap-double)
);
--camera-height: calc(
var(--d-carousel-item-height) + var(--d-carousel-item-gap-double)
);
position: relative;
width: var(--camera-width);
height: var(--camera-height);
perspective: var(--d-carousel-perspective);
pointer-events: none;
.d-carousel {
position: absolute;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transform: translateZ(var(--d-carousel-translate-z)) rotateX(var(--d-carousel-rotate-x));
transition: var(--d-carousel-transition);
pointer-events: none;
&-item {
--d-carousel-item-rotate-x: calc(var(--i) * var(--d-carousel-item-deg));
--d-carousel-item-translate-z: var(--d-carousel-item-r);
position: absolute;
left: var(--d-carousel-item-gap);
top: var(--d-carousel-item-gap);
width: var(--d-carousel-item-width);
height: var(--d-carousel-item-height);
border-radius: 5px;
backface-visibility: hidden;
pointer-events: none;
transform: rotateX(var(--d-carousel-item-rotate-x))
translateZ(var(--d-carousel-item-translate-z));
}
}
}
.tip {
position: absolute;
top: 4rem;
}
View Compiled
const getTanFromDegrees = (deg: number) => Math.tan((deg * Math.PI) / 180);
const camera = document.querySelector(".camera") as HTMLElement;
const cameraHeight = camera.offsetHeight;
const swiper = document.querySelector(".swiper");
const swiperHeight = (swiper as HTMLElement).offsetHeight;
const dCarousel = document.querySelector(".d-carousel") as HTMLElement;
const dCarouselItems = dCarousel.children;
const dCarouselItemCount = dCarouselItems.length;
const dCarouselItemDeg = 360 / dCarouselItemCount;
const dCarouselItemTanDegHalf = Math.tan(
getTanFromDegrees(dCarouselItemDeg / 2)
);
const dCarouselItemR = cameraHeight / 2 / dCarouselItemTanDegHalf;
dCarousel.style.setProperty("--d-carousel-item-r", `${dCarouselItemR}px`);
dCarousel.style.setProperty("--d-carousel-item-deg", `${dCarouselItemDeg}deg`);
Array.from(dCarouselItems).forEach((item, i) =>
(item as HTMLElement).style.setProperty("--i", `${i}`)
);
let swipeDeg = 0;
let previousDeltaY = 0;
let deltaYDelta = 0;
const manager = new Hammer.Manager(swiper);
const Pan = new Hammer.Pan({ threshold: 10 });
manager.add(Pan);
manager.on("pan", (e) => {
const deltaY = e.deltaY;
deltaYDelta = deltaY - previousDeltaY;
previousDeltaY = deltaY;
const direction = e.offsetDirection;
if (direction === 8 || direction === 16) {
const swipeDegDelta = (deltaYDelta / swiperHeight) * 360;
swipeDeg -= swipeDegDelta;
dCarousel.style.setProperty("--d-carousel-rotate-x", `${swipeDeg}deg`);
}
if (e.isFinal) {
previousDeltaY = 0;
}
});
const disableTouchMove = () => {
document.body.addEventListener(
"touchmove",
(e) => {
e.preventDefault();
},
{ passive: false }
);
};
disableTouchMove();
View Compiled