【问题标题】:How to create a vertical carousel using plain JavaScript and CSS如何使用纯 JavaScript 和 CSS 创建垂直轮播
【发布时间】:2015-11-11 20:13:21
【问题描述】:

我正在尝试使用原生 JavaScript 和 CSS 创建一个垂直轮播。我知道 jQuery 有一个轮播库,但我想尝试不使用外部库从头开始构建它。我一开始只是尝试移动顶部图像,然后我计划继续移动下一个图像。我卡在第一张图片上。这是我需要你帮助的地方,StackOverflowers。

我的 HTML:

<div class="slider vertical" >
    <img class="first opened" src="http://malsup.github.io/images/beach1.jpg">
    <img class="opened" src="http://malsup.github.io/images/beach2.jpg">
    <img src="http://malsup.github.io/images/beach3.jpg">
    <img src="http://malsup.github.io/images/beach4.jpg">
    <img src="http://malsup.github.io/images/beach5.jpg">
    <img src="http://malsup.github.io/images/beach9.jpg">
</div>
<div class="center">
    <button id="prev">∧ Prev</button>
    <button id="next">∨ Next</button>
</div>

JavaScript:

var next = document.getElementById('next');
var target = document.querySelector('.first');

next.addEventListener('click', nextImg, false);

function nextImg(){
     if (target.classList.contains('opened')) {
        target.classList.remove('opened');
        target.classList.add('closed');
    } else {
        target.classList.remove('closed');
        target.classList.add('opened');
    }
}

CSS:

div.vertical {
    width: 100px;
}

.slider {
    position: relative;
    overflow: hidden;
    height: 250px;

        -webkit-box-sizing:border-box;
       -moz-box-sizing:border-box;
        -ms-box-sizing:border-box;
            box-sizing:border-box;

    -webkit-transition:-webkit-transform 1.3s ease;
       -moz-transition:   -moz-transform 1.3s ease;
        -ms-transition:    -ms-transform 1.3s ease;
            transition:        transform 1.3s ease;
}

.slider img {
    width: 100px;
    height: auto;
    padding: 2px;
}

.first.closed{
    /* partially offscreen */
    -webkit-transform: translate(0, -80%);
       -moz-transform: translate(0, -80%);
        -ms-transform: translate(0, -80%);
            transform: translate(0, -80%);
}

.first.opened{
    /* visible */
    -webkit-transform: translate(0, 0%);
       -moz-transform: translate(0, 0%);
        -ms-transform: translate(0, 0%);
            transform: translate(0, 0%);
}

我的想法是:

  1. 使用类来移动和显示内容
  2. 使用 JavaScript 添加和删除类

我想我可能没有正确地分解问题。

这就是我想要的样子:http://jsfiddle.net/natnaydenova/7uXPx/

这是我糟糕的尝试:http://jsfiddle.net/6cb58pkr/

【问题讨论】:

  • 顺便说一句,在您的 JS Fiddle 中:不要将 getElementById('&lt;id&gt;') 的语法与 querySelector('#&lt;id&gt;') 的语法混淆;否则您会收到消息(在控制台中):Uncaught TypeError: Cannot read property 'addEventListener' of null。我已经更正了 那个 错误并更新了演示:jsfiddle.net/davidThomas/6cb58pkr/1
  • 谢谢@David Thomas 就是这样!这样做的时候一定是在睡觉。
  • 这种实现所需的大部分(如果不是全部)主要 css 方面,我认为您已经包括在内。 (特别是固定高度和宽度,隐藏溢出)。不过我要说的一件事是,也许您的 javascript 方法还应该负责查看/滚动列表中的位置——这有助于您支持任意数量、任意尺寸的图像。我最近也在使用 css3 flex。可以帮助布局吗?抱歉,它有点模糊的建议。祝你好运。 :)
  • 感谢@ne1410s 的建议,我会在这个问题上睡一觉,并研究一个比第一张图像移动更多的更好的解决方案。

标签: javascript html css animation carousel


【解决方案1】:

使用 CSS transform 属性的替代方法是在包装器 div 内提供轮播绝对定位并操纵轮播的 top 属性。然后,您可以使用任何您喜欢的缓动函数来为滑动运动设置动画。在下面的sn-p中,我使用cubic easing in/out

需要注意的一个棘手的事情是旋转图像和执行滑动动画的顺序。当您想显示下面的下一张图片时,您必须:

  • 将旋转木马向上滑动一个相框的​​高度
  • 将第一张图片旋转到末尾
  • 将轮播的垂直偏移重置为零

要显示上面的下一张图片:

  • 将最后一张图片旋转到开头
  • 立即将旋转木马向上移动一个相框的​​高度
  • 向下滑动轮播,直到其垂直偏移量为零

在下面的sn-p中,可以通过调整脚本顶部的Carousel.width来设置轮播的宽度。 (虽然图像高度不必与图像宽度相同,但我确实假设所有图像都具有相同的尺寸。)您也可以使用Carousel.numVisibleCarousel.duration 参数。

var Carousel = {
  width: 100,     // Images are forced into a width of this many pixels.
  numVisible: 2,  // The number of images visible at once.
  duration: 600,  // Animation duration in milliseconds.
  padding: 2      // Vertical padding around each image, in pixels.
};

function rotateForward() {
  var carousel = Carousel.carousel,
      children = carousel.children,
      firstChild = children[0],
      lastChild = children[children.length - 1];
  carousel.insertBefore(lastChild, firstChild);
}
function rotateBackward() {
  var carousel = Carousel.carousel,
      children = carousel.children,
      firstChild = children[0],
      lastChild = children[children.length - 1];
  carousel.insertBefore(firstChild, lastChild.nextSibling);
}

function animate(begin, end, finalTask) {
  var wrapper = Carousel.wrapper,
      carousel = Carousel.carousel,
      change = end - begin,
      duration = Carousel.duration,
      startTime = Date.now();
  carousel.style.top = begin + 'px';
  var animateInterval = window.setInterval(function () {
    var t = Date.now() - startTime;
    if (t >= duration) {
      window.clearInterval(animateInterval);
      finalTask();
      return;
    }
    t /= (duration / 2);
    var top = begin + (t < 1 ? change / 2 * Math.pow(t, 3) :
                               change / 2 * (Math.pow(t - 2, 3) + 2));
    carousel.style.top = top + 'px';
  }, 1000 / 60);
}

window.onload = function () {
  document.getElementById('spinner').style.display = 'none';
  var carousel = Carousel.carousel = document.getElementById('carousel'),
      images = carousel.getElementsByTagName('img'),
      numImages = images.length,
      imageWidth = Carousel.width,
      aspectRatio = images[0].width / images[0].height,
      imageHeight = imageWidth / aspectRatio,
      padding = Carousel.padding,
      rowHeight = Carousel.rowHeight = imageHeight + 2 * padding;
  carousel.style.width = imageWidth + 'px';
  for (var i = 0; i < numImages; ++i) {
    var image = images[i],
        frame = document.createElement('div');
    frame.className = 'pictureFrame';
    var aspectRatio = image.offsetWidth / image.offsetHeight;
    image.style.width = frame.style.width = imageWidth + 'px';
    image.style.height = imageHeight + 'px';
    image.style.paddingTop = padding + 'px';
    image.style.paddingBottom = padding + 'px';
    frame.style.height = rowHeight + 'px';
    carousel.insertBefore(frame, image);
    frame.appendChild(image);
  }
  Carousel.rowHeight = carousel.getElementsByTagName('div')[0].offsetHeight;
  carousel.style.height = Carousel.numVisible * Carousel.rowHeight + 'px';
  carousel.style.visibility = 'visible';
  var wrapper = Carousel.wrapper = document.createElement('div');
  wrapper.id = 'carouselWrapper';
  wrapper.style.width = carousel.offsetWidth + 'px';
  wrapper.style.height = carousel.offsetHeight + 'px';
  carousel.parentNode.insertBefore(wrapper, carousel);
  wrapper.appendChild(carousel);
  var prevButton = document.getElementById('prev'),
      nextButton = document.getElementById('next');
  prevButton.onclick = function () {
    prevButton.disabled = nextButton.disabled = true;
    rotateForward();
    animate(-Carousel.rowHeight, 0, function () {
      carousel.style.top = '0';
      prevButton.disabled = nextButton.disabled = false;
    });
  };
  nextButton.onclick = function () {
    prevButton.disabled = nextButton.disabled = true;
    animate(0, -Carousel.rowHeight, function () {
      rotateBackward();
      carousel.style.top = '0';
      prevButton.disabled = nextButton.disabled = false;
    });
  };
};
body {
  font-family: sans-serif;
}
.buttons {
  margin: 5px 0;
}
button {
  font-size: 14px;
  display: inline;
  padding: 3px 6px;
  border: 2px solid #ccc;
  background: #fff;
  border-radius: 5px;
  outline: none;
}
button:hover {
  border: 2px solid #888;
  background: #ffe;
  cursor: pointer;
}
#carouselWrapper {
  position: relative;
  overflow: hidden;
}
#carousel {
  position: absolute;
  visibility: hidden;
}
<div id="spinner"> 
  Loading...
</div>

<div id="carousel">
  <img src="http://malsup.github.io/images/beach1.jpg">
  <img src="http://malsup.github.io/images/beach2.jpg">
  <img src="http://malsup.github.io/images/beach3.jpg">
  <img src="http://malsup.github.io/images/beach4.jpg">
  <img src="http://malsup.github.io/images/beach5.jpg">
  <img src="http://malsup.github.io/images/beach9.jpg">
</div>

<div class="buttons">
  <button id="prev">&uarr; Prev</button>
  <button id="next">&darr; Next</button>
</div>

【讨论】:

  • 感谢@Michael Laszlo 提供了一个优雅的解决方案并解释了您的思考过程,我认为这是最有价值的部分。现在一切正常。
  • @Michael 如果我不想循环,如何让你的代码停止?
  • @NikhilKapoor 您可以将索引属性添加到轮播对象。您必须在 rotateForward 中增加它并在 rotateBackward 中减少它。然后,在这些函数开始时,您可以检查索引,如果索引处于限制状态,则提前返回。
  • @Michael 谢谢我正在使用带角度的代码并且它显示空白然后突然出现图片。有什么方法可以聊天吗?
猜你喜欢
  • 1970-01-01
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 2015-11-28
  • 2020-12-10
相关资源
最近更新 更多