36

我想知道使用 JavaScript/jQuery 为网站构建 Infinity-Image-Loop-Slider 的最佳(良好可读代码、害虫实践代码、可重用性)概念是什么?我不知道如何对幻灯片进行编码,但什么蓝图符合上述要求。我的问题的主要焦点是如何排列图片以获得无限循环滑块的印象。

通过查看来自不同滑块的代码,我遇到了两种解决方案:

- 每次显示下一张/上一张图像时更改所有图像的 z-Index。

- 更改图像在 DOM 中的位置。

但是检查和理解其他人的代码非常耗时 - 这就是我问这个问题的原因:-)

4

4 回答 4

75

tl;dr - JSBin 示例:http: //jsbin.com/ufoceq/8/


一种无需太多努力即可创建无限图像滑块的简单方法如下:为了简单起见,假设您有<n>要在循环中滑动的图像,以便在n第 th图像之后可视化的是1st(和反之亦然)。

这个想法是创建第一个和最后一个图像的克隆,以便

  • 最后一个图像的克隆在第一个图像之前添加;
  • 第一个图像的克隆附加在最后一个图像之后。

无论您的图像数量是多少,您最多只需要附加 2 个克隆元素。

再次为简单起见,假设所有图像都是100px宽的,并且它们被包裹在一个容器中,您可以将其左/右移动到一个带有overflow: hidden. 然后,所有图像都可以轻松地与容器对齐display: inline-blockwhite-space: nowrap设置在容器上(flexbox现在更容易了)。

对于n = 4DOM 结构将是这样的:

offset(px)     0       100     200     300     400     500
images         |   4c   |   1   |   2   |   3   |   4   |   1c
                                                   
/*                 ^^                                       ^^
       [ Clone of the last image ]              [ Clone of the 1st image ]
*/

一开始,您的容器将被定位left: -100pxmargin-left: -100px 或者甚至更好(出于性能问题) transform: translateX(-100px)),因此滑块可以显示第一张图像。要从一个图像切换到另一个图像,您需要在您之前选择的相同属性上应用一个 javascript 动画。

当您的滑块当前位于第 4图像时,您必须从 image 切换41c,因此想法是在动画结束时执行回调,很快将您的滑块包装重新定位在真正的第 1 个图像偏移量(例如,您设置left: -100px到容器)

当您的滑块当前位于第一个元素上时,这类似于要显示上一个图像,您只需要从图像执行动画14c当动画完成时,您只需移动容器,使滑块立即定位在 4 th图像偏移量(例如,您设置left: -400px为容器)。


您可以在上面的小提琴上看到效果:这是js/jquery我使用的最小代码(当然代码甚至可以优化,因此项目的宽度不会硬编码到脚本中)

$(function() {
 
  var gallery = $('#gallery ul'),
      items   = gallery.find('li'),
      len     = items.length,
      current = 1,  /* the item we're currently looking */
      
      first   = items.filter(':first'),
      last    = items.filter(':last'),
      
      triggers = $('button');
  
  /* 1. Cloning first and last item */
  first.before(last.clone(true)); 
  last.after(first.clone(true)); 
  
  /* 2. Set button handlers */
  triggers.on('click', function() {

    var cycle, delta;
    
    if (gallery.is(':not(:animated)')) {
     
        cycle = false;
        delta = (this.id === "prev")? -1 : 1;
        /* in the example buttons have id "prev" or "next" */  
    
        gallery.animate({ left: "+=" + (-100 * delta) }, function() {
      
            current += delta;
       
            /** 
             * we're cycling the slider when the the value of "current" 
             * variable (after increment/decrement) is 0 or when it exceeds
             * the initial gallery length
             */          
            cycle = (current === 0 || current > len);
       
            if (cycle) {
                /* we switched from image 1 to 4-cloned or 
                   from image 4 to 1-cloned */
                current = (current === 0)? len : 1; 
                gallery.css({left:  -100 * current });
            }
        });   
     }
    
  });
});

如前所述,这个解决方案不需要太多的努力和谈论性能,将这种方法与没有循环的普通滑块进行比较,它只需要在滑块初始化时进行两次额外的 DOM 插入和一些(相当微不足道的)额外逻辑管理向后/向前循环。

这是另一个同时看到两个元素的示例:在这种情况下,您需要克隆更多元素并对逻辑进行一些简单的更改

https://codepen.io/fcalderan/pen/bGbjZdz

我不知道是否存在更简单或更好的方法,但无论如何希望这会有所帮助。

注意:如果您还需要一个响应式画廊,也许这个答案也可能有帮助

于 2013-04-08T10:53:12.753 回答
3

我刚刚创建了项目滑块:检查一下: https ://github.com/lingtalfi/jItemSlider/blob/master/README.md

600行代码,或许你可以简单浏览一下。

其背后的想法受到 netflix 滑块的启发(截至 2016 年 2 月 24 日)。

基本上,它使用 css 转换翻译,因为这些是浏览器中最快/最流畅的。

http://eng.wealthfront.com/2015/05/19/performant-css-animations/

现在滑动运动背后的基本概念是,您只显示当前可见切片,但左侧还有一个不可见切片,右侧还有另一个不可见切片。

而且,你还有两个额外的物品,每边一个,所以你的物品看起来像这样:

上一个项目 - 上一个额外项目 - 主要项目 - 下一个额外项目 - 下一个项目

只有主要项目是可见的。额外的项目是部分可见的。上一个和下一个项目是不可见的。

更多细节在这里: https ://github.com/lingtalfi/jItemSlider/blob/master/doc/conception.md

现在,当您向右滑动时(例如),您基本上将更多项目附加到右侧,然后从左侧删除这些项目。

这种技术是迄今为止我遇到的最棒的技术,因为您不需要处理一长串项目(使用克隆而不删除不可见项目),这会使您的动画变慢。

注意:我第一次尝试这个滑块实际上是克隆而不删除,它可以工作,但我不喜欢它: https ://github.com/lingtalfi/jInfiniteSlider

此外,它是基于项目的(而不是基于像素的),最终,这就是用户所期望的,因为一切总是对齐的,因为它应该是。

于 2016-02-24T12:40:08.617 回答
0

非常感谢这篇文章!我已经更新并使用了上面的代码。我希望这对每个人都有帮助。可怜的开发商。

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Directive slider</title>
    <style>
        /* 四联切换焦点图 */
        .slides-wrapper{ position: relative;  width: 100%;  margin: 10px 0; }
        .gallery { position: relative;  width: 1200px;  height: 180px;  overflow:hidden;  }
        .gallery ul {  font-size: 0;  white-space: nowrap;  position: absolute;  top: 0;  left: -1200px;  margin: 0; padding: 0;  }
        .gallery li {  display: inline-block;  vertical-align: top;  width: 1200px;  height: 180px;  white-space: normal;  }
        .gallery li img{  width: 298px;  height:180px;  padding: 1px;  }
        .gallery .arrow { background: url(/shop/templates/default/images/home_bg.png) no-repeat; background-size: 150px 223px; width: 35px; height: 70px; position: absolute; z-index: 2; top: 50px; cursor: pointer; opacity: 0;}
        .gallery .prev { background-position: 1px -92px; left: 0;}
        .gallery .next { background-position: -30px -92px; right: 0px;}
    </style>
    <style type="text/css">
        .demo_wrapper{
            margin: 0 auto;
            width: 1200px;
        }
        .demo_wrapper .title{
            text-align: center;
        }
    </style>
</head>
<body>
<div class="demo_wrapper">
    <div class="title">
        <h1>Directive slider (Published by fenmingyu)</h1>
    </div>
    <!-- demo content -->
    <div class="slides-wrapper">
        <div class="gallery" id="top_sale_gallery">
            <ul>
                <li>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-1.jpg?234" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-2.jpg?752" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-3.jpg?320" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-4.jpg?365" alt=""></a>
                </li>
                <li>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-1.jpg?852" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-2.jpg?746" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-3.jpg?525" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-4.jpg?550" alt=""></a>
                </li>
            </ul>
            <div class='arrow prev'></div>
            <div class='arrow next'></div>
        </div>
        <div class="gallery" id="top_goods_gallery">
            <ul>
                <li>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-3-1.jpg?793" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-3-2.jpg?180" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-3-3.jpg?550" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-3-4.jpg?851" alt=""></a>
                </li>
                <li>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-1.jpg?234" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-2.jpg?752" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-3.jpg?320" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-1-4.jpg?365" alt=""></a>
                </li>
                <li>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-1.jpg?852" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-2.jpg?746" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-3.jpg?525" alt=""></a>
                    <a href="" target="_blank" title="" style="opacity: 1;"><img src="http://imgserv.5thmedia.cn/upload_test/shop/editor/web-102-104-2-4.jpg?550" alt=""></a>
                </li>
            </ul>
            <div class='arrow prev'></div>
            <div class='arrow next'></div>
        </div>
        <div style="clear: both;"></div>
    </div>
</div>

</body>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
    $(function() {
        $.fn.gallery = function(settings) {
            var defaults = {
                time: 3000,
                direction:1
            };
            var settings = $.extend(defaults, settings);
            var gallery_wrapper = $(this),
                gallery = gallery_wrapper.find('ul'),
                items   = gallery.find('li'),
                len     = items.length,
                current = 1,  /* the current item we're looking */
                first   = items.filter(':first'),
                last    = items.filter(':last'),
                w = gallery.find('li').width(),
                triggers = gallery_wrapper.find('.arrow');
            var show_slide = function(direction,w){
                gallery.animate({ left: "+=" + (-w * direction) }, function() {

                    current += direction;

                    /**
                     * we're cycling the slider when the the value of "current"
                     * variable (after increment/decrement) is 0 or when it exceeds
                     * the initial gallery length
                     */
                    cycle = !!(current === 0 || current > len);

                    if (cycle) {
                        /* we switched from image 1 to 4-cloned or
                         from image 4 to 1-cloned */
                        current = (current === 0)? len : 1;
                        gallery.css({left:  -w * current });
                    }
                });
            };
            var picTimer = setInterval(function() {
                        show_slide(settings.direction,w);
                    },
                    settings.time);
            return this.each(function(){

                /* 1. Cloning first and last item */
                first.before(last.clone(true));
                last.after(first.clone(true));
                /* 2. Set button handlers */
                triggers.on('click', function() {
                    if (gallery.is(':not(:animated)')) {

                        var cycle = false;
                        settings.direction = ($(this).hasClass('prev'))? -1 : 1;
                        /* in the example buttons have id "prev" or "next" */
                        show_slide(settings.direction,w);
                    }
                    clearInterval(picTimer);
                    picTimer = setInterval(function() {
                                show_slide(settings.direction,w);
                            },
                            settings.time);
                });
                /* hover show arrows*/
                show_slide(settings.direction,w);

                gallery_wrapper.hover(function() {
                    $(this).find(".arrow").css("opacity", 0.0).stop(true, false).animate({
                                "opacity": "0.3"
                            },
                            300);
                },function(){
                    $(this).find(".arrow").css("opacity", 0.3).stop(true, false).animate({
                                "opacity": "0"
                            },
                            300);
                });
            });
        };
        $('#top_goods_gallery.gallery').gallery();
        $('#top_sale_gallery.gallery').gallery({
            time: 5000,
            direction:-1
        });
    });
</script>
</html>

te 并在我的项目中使用它。

于 2015-06-19T15:43:31.123 回答
0

香草Javascript!无克隆技术,getElementsByClassName 来拯救

document.getElementsByClassName 选择是一个实时集合;与 querySelector 方法不同,DOM 中的任何更改都会在存储的变量中更新。

在这种技术中,如果我们在右击时到达最后一张幻灯片,我们只需将第一张幻灯片移到末尾,或者当我们在左击时到达第一张幻灯片时,我们将最后一张幻灯片移到开头。此处不需要创建克隆。getElementsByClassName 方法为我们提供了一个实时的 HTML 集合,当我们在 DOM 中进行更改时,可以使用该集合进行更新。(在这种情况下,改变 div 的顺序)

这是我的GitHub 存储库

// slider
const slides = document.getElementsByClassName("slide"); // this selection is a live collection; any changes in DOM is updated in the variable unlike querySelectors
const btnLeft = document.querySelector(`.btn-left`);
const btnRight = document.querySelector(`.btn-right`);

let currentSlideIndex = 0;
let lastSlideIndex = slides.length - 1;

// go to a slide;
function goToSlide(slideIndex) {
    [...slides].forEach((s, i) => {
        s.style.transform = `translateX(${100 * (i - slideIndex)}%)`
    })
    currentSlideIndex = slideIndex;
}
goToSlide(currentSlideIndex);

// make ready the next slide if current slide is the first or the last slide
function readyNextSlide() {
    // if currentSlide is the last slide, shift the first slide to the end
    if (currentSlideIndex === lastSlideIndex) {
        slides[lastSlideIndex].insertAdjacentElement("afterend", slides[0]);
        slides[lastSlideIndex].style.transform = `translateX(${100}%)`;
        currentSlideIndex--; //this is because current slide is now the second last slide
    }
    // if currentSlide is the first slide, shift the last slide to the beginning
    if (currentSlideIndex === 0) {
        slides[0].insertAdjacentElement("beforebegin", slides[lastSlideIndex]);
        slides[0].style.transform = `translateX(-${100}%)`;
        currentSlideIndex++; //this is because current slide is now the second slide
    }
}

// put the last slide in the beginning; ('if' condition is not necessary but providing if condition is future proof if user sets the initial slide to be shown as the last slide )
if (currentSlideIndex === lastSlideIndex || currentSlideIndex === 0) readyNextSlide();

// shift all slides left or right based on direction provided
function shiftSlides(direction) {
    direction ? currentSlideIndex++ : currentSlideIndex--
    if (currentSlideIndex === lastSlideIndex || currentSlideIndex === 0) readyNextSlide();
    goToSlide(currentSlideIndex);
}

//button click events
btnRight.addEventListener("click", shiftSlides.bind(null, 1));
btnLeft.addEventListener("click", shiftSlides.bind(null, 0));
body {
  display: grid;
  height: 100vh;
  width: 100vw;
  align-items: center;
  align-content: center;
  justify-content: center;
}

.slider {
  position: relative;
  width: 600px;
  height: 300px;
  transform: scale(0.8);
  overflow: hidden; /* remove overflow to see what's going on*/
}

.slide {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 1s;
}

.slide b {
  position: absolute;
  font-size: 10em;
  color: black;
  opacity: 0.6;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.s1 {
  background-color: cornflowerblue;
}

.s2 {
  background-color: bisque;
}

.s3 {
  background-color: coral;
}

.s4 {
  background-color: thistle;
}

.btn {
  position: absolute;
  top: 50%;
  z-index: 10;
  border: none;
  background: crimson;
  font-family: inherit;
  color: white;
  height: 5.5rem;
  width: 5.5rem;
  font-size: 3.25rem;
  cursor: pointer;
}

.btn-left {
  left: 6%;
  transform: translate(-50%, -50%);
}

.btn-right {
  right: 6%;
  transform: translate(50%, -50%);
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Infinity Looping Rotating slider</title>
        <link rel="stylesheet" href="slider.css">
        <script src="slider.js" defer></script>
    </head>
    <body>
        <div class="slider">
            <div class="slide s1"><b>1</b></div>
            <div class="slide s2"><b>2</b></div>
            <div class="slide s3"><b>3</b></div>
            <div class="slide s4"><b>4</b></div>

            <button class="btn btn-left">&larr;</button>
            <button class="btn btn-right">&rarr;</button>
        </div>
        <p>
            <b>
                This is my response to a questing in <a href="https://stackoverflow.com/a/70340585/6216765">StackOverflow</a> about infinity loop slider.<br>
                My github repo is <a href="https://github.com/dibakash/infinity-looping-rotating-slider">Infinity loop Slider by Dibakash</a>
            </b>
        </p>

    </body>
</html>

于 2021-12-13T20:33:38.523 回答