如何用纯JS和CSS创建一个带触摸的无限轮播? [已关闭]

问题描述 投票:0回答:1

这是一个使用普通 JS 和 CSS 进行触摸的无限多项目图像轮播的示例。

没有依赖项,没有框架。

javascript responsive-design carousel gallery infinity
1个回答
0
投票

在最近的一个项目中,我实现了一个图像轮播,重点关注效率和响应能力,我想分享我的设计和编码决策背后的基本原理。让我们来分解一下:

  • HTML 结构

    轮播的结构是包含所有轮播图像的

    <div>
    。值得注意的是,我对第一张和最后三张图像使用了“急切”加载。这是因为当轮播最初加载或用户导航到末尾时,这些图像可能位于视口中或视口附近。预加载可确保尽快加载这些图像,从而增强用户体验。

  • CSS设计

    我使用 CSS 自定义属性(变量)来更轻松地维护和更好的可扩展性。响应式设计是使用媒体查询来实现的。低于 576 像素宽度时,轮播一次显示一张图像(100% 宽度),高于该宽度时,一次显示三张图像(33.33% 宽度)。较小屏幕上的导航按钮

    display: none
    通过提供更多空间来增强移动用户体验。

  • JavaScript 功能

    JavaScript 类

    Carousel
    处理所有轮播功能。它使用设置为第一个真实图像的当前索引进行初始化,并考虑循环效果的重复(假)图像。
    slide
    函数更新索引并使用 CSS 过渡来实现平滑移动。特别关注触摸导航,这是现代网络界面的必备功能,允许用户通过滑动来导航轮播。

  • 关键考虑因素

    1. 性能:对非立即图像使用“延迟”加载可确保我们不会使初始页面加载过载,这对于性能至关重要,尤其是在移动设备上。
    2. 响应能力:轮播适应不同的屏幕尺寸,确保跨设备的用户体验一致。
    3. 用户体验:某些图像的预加载和触摸导航支持使轮播变得用户友好。
    4. 可维护性:CSS 变量和结构良好的 JavaScript 类使将来的修改变得更容易。

此实现符合注重性能、响应能力和用户体验的现代 Web 开发标准。

<div class="image-carousel">

  <div class="carousel-viewport">

    <!--/ You need to fake last three items (use eager loading!) /-->
    <div class="carousel-image"><img src="https://i.ibb.co/Gv9jq4z/sci-fi-scene-ossenbrueck-4.png" class="img-fluid" alt="..." loading="eager"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/dQmdmM3/sci-fi-scene-ossenbrueck-5.png" class="img-fluid" alt="..." loading="eager"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/LprBxDc/sci-fi-scene-ossenbrueck-6.png" class="img-fluid" alt="..." loading="eager"></div>
      
    <!--/ All images/-->
    <div class="carousel-image"><img src="https://i.ibb.co/Kr8N8D4/sci-fi-scene-ossenbrueck-1.png" class="img-fluid" alt="..." loading="lazy"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/pXvLw1D/sci-fi-scene-ossenbrueck-2.png" class="img-fluid" alt="..." loading="lazy"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/6Dg7bY1/sci-fi-scene-ossenbrueck-3.png" class="img-fluid" alt="..." loading="lazy"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/Gv9jq4z/sci-fi-scene-ossenbrueck-4.png" class="img-fluid" alt="..." loading="lazy"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/dQmdmM3/sci-fi-scene-ossenbrueck-5.png" class="img-fluid" alt="..." loading="lazy"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/LprBxDc/sci-fi-scene-ossenbrueck-6.png" class="img-fluid" alt="..." loading="lazy"></div>

    <!--/ You need to fake first three items (use eager loading!) /-->
    <div class="carousel-image"><img src="https://i.ibb.co/Kr8N8D4/sci-fi-scene-ossenbrueck-1.png" class="img-fluid" alt="..." loading="eager"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/pXvLw1D/sci-fi-scene-ossenbrueck-2.png" class="img-fluid" alt="..." loading="eager"></div>
    <div class="carousel-image"><img src="https://i.ibb.co/6Dg7bY1/sci-fi-scene-ossenbrueck-3.png" class="img-fluid" alt="..." loading="eager"></div>
  </div>

  <button class="carousel-control prev">Prev</button>
  <button class="carousel-control next">Next</button>
</div>
document.addEventListener('DOMContentLoaded', () => {

    class Carousel {

        constructor(viewportSelector, prevSelector, nextSelector) {

            this.viewport = document.querySelector(viewportSelector);
            this.prevButton = document.querySelector(prevSelector);
            this.nextButton = document.querySelector(nextSelector);
            this.currentIndex = 3;
            this.realImageCount = this.viewport.children.length / 2;

            this.setupNavigation();
            this.setupTouchNavigation();

            this.renderViewport();
        }

        getImageWidth() {
            return window.innerWidth <= 600 ? 100 : 33.33;
        }

        resetPositionFirst() {

            this.viewport.classList.add('transition-disabled');
            this.currentIndex = 3;
            this.renderViewport();
            this.viewport.offsetHeight; // Trigger reflow
            this.viewport.classList.remove('transition-disabled');

            this.slide(1);
        }

        resetPositionToEnd() {

            this.viewport.classList.add('transition-disabled');
            this.currentIndex = this.viewport.children.length - 6;
            this.renderViewport();
            this.viewport.offsetHeight; // Trigger reflow
            this.viewport.classList.remove('transition-disabled');

            this.slide(-1);
        }

        slide(direction) {

            this.currentIndex += direction;

            if (this.currentIndex > this.realImageCount + 4) {
                this.resetPositionFirst();
            } else if (this.currentIndex < 0) {
                this.resetPositionToEnd();
            } else {
                this.renderViewport();
            }
        }

        renderViewport() {
            this.viewport.style.transform = `translateX(-${this.currentIndex * this.getImageWidth()}%)`;
        }

        setupNavigation() {
            this.prevButton.addEventListener('click', () => this.slide(-1));
            this.nextButton.addEventListener('click', () => this.slide(1));
        }

        setupTouchNavigation() {

            let touchStartX = 0;
            let touchEndX = 0;

            this.viewport.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX, {passive: true});
            this.viewport.addEventListener('touchmove', e => touchEndX = e.touches[0].clientX, {passive: true});

            this.viewport.addEventListener('touchend', () => {
                if (touchStartX - touchEndX > 50) this.slide(1);
                if (touchEndX - touchStartX > 50) this.slide(-1);
            });
        }
    }

    new Carousel('.carousel-viewport', '.prev', '.next');
});

:root {
    --carousel-control-bg: #fff;
    --carousel-img-width: 33.33%;
    --carousel-mobile-img-width: 100%;
}

.img-fluid {
  width: 100%;
  height: auto;
}

.image-carousel {
    position: relative;
    width: 100%;
    overflow: hidden;
}

.carousel-viewport {
    display: flex;
    transition: transform 0.4s ease;
}

.carousel-viewport .carousel-image {
    flex: 0 0 var(--carousel-mobile-img-width);
    max-width: var(--carousel-mobile-img-width);
}

.carousel-control {
    display: none;
    position: absolute;
    top: 50%;
    background-color: #fff;
    border: none;
    cursor: pointer;
    padding: 10px;
    transform: translateY(-50%);
    z-index: 2;
}

.carousel-control.prev {
    left: 10px;
}

.carousel-control.next {
    right: 10px;
}

.transition-disabled {
    transition: none !important;
}

@media (min-width: 576px) {

    .carousel-viewport .carousel-image {
        flex: 0 0 var(--carousel-img-width);
        max-width: var(--carousel-img-width);
    }

    .carousel-control {
        display: block;
    }
}

代码笔

© www.soinside.com 2019 - 2024. All rights reserved.