【问题标题】:create loop of moving images inside a div在 div 中创建动态图像循环
【发布时间】:2021-05-26 12:45:10
【问题描述】:

我想建立一个像this 这样的图片库,其中图片会自动循环播放,但同时也可以使用鼠标滚轮在图库中进行交互。我也会实现类似图像的效果,而不是滚动到屏幕之外的一侧。

所以我试图了解从哪里开始,但我找不到任何用于该任务的库,所以我从头开始尝试使用 vanilla js,这是我的尝试。

var testArray = document.querySelectorAll(".image");
var totImg = testArray.length;

var contIterazioni = 0;
var test = testArray[contIterazioni];
var bounding = test.getBoundingClientRect();



 function LoopFunction() {
     setInterval(galleryScroll, 20);
 }
 function galleryScroll() {
     test = testArray[contIterazioni];
     bounding = test.getBoundingClientRect();
     if (bounding.left == 0) {
      //console.log("immagine bordo sx");
      if (test.width > 0) {
        test.width = test.width - .25;
      } else {
        contIterazioni = contIterazioni + 1;
        if (contIterazioni >= totImg){
          contIterazioni = 0;
        }
      } 
    }
 }

LoopFunction();
body{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.container {
  width: 100vw;
  height: 100vh;
  display: flex;
  overflow: hidden;
}
<div class="container">
  <img src="https://media-cdn.tripadvisor.com/media/photo-s/0e/eb/ad/3d/crazy-cat-cafe.jpg" alt="" class="image">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Chairman_Meow_Bao.jpg/1200px-Chairman_Meow_Bao.jpg" alt="" class="image">
  <img src="https://static.scientificamerican.com/sciam/cache/file/92E141F8-36E4-4331-BB2EE42AC8674DD3_source.jpg" alt="" class="image">
  <img src="https://cdn.britannica.com/91/181391-050-1DA18304/cat-toes-paw-number-paws-tiger-tabby.jpg" alt="" class="image">
  <img src="https://www.orlandocatcafe.com/wp-content/uploads/2020/07/14.png" alt="" class="image">
  
</div>

不幸的是,我不知道如何循环播放图像。你能帮我理解怎么做吗?你认为我的方法可以是一个好的方法吗?或者您会推荐其他方法来获得这种效果?

谢谢!

【问题讨论】:

标签: javascript jquery carousel gsap horizontal-scrolling


【解决方案1】:

当您遍历所有图像以将其宽度设置为其初始值时,请使用 for 循环。还要从轮播的开头添加额外的三张图片,这样在轮播结束时就不会出现空白。

    var testArray = document.querySelectorAll(".image");
    var totImg = testArray.length;

    var contIterazioni = 0;
    var test = testArray[contIterazioni];
    var bounding = test.getBoundingClientRect();

     var imgsWidths = []
     window.onload = () => {
       for(i = 0; i < testArray.length; i++) {
           imgsWidths[i] = testArray[i].width
       }
    }
     
   function galleryScroll() {
       test = testArray[contIterazioni];
       bounding = test.getBoundingClientRect();
       if (bounding.left == 0) {
        if (test.width > 0) {
          test.width = test.width - 2;
        } else {
          contIterazioni = contIterazioni + 1;
          if (contIterazioni >= totImg - 3){
            for(i = 0; i < testArray.length; i++) {
              testArray[i].width = imgsWidths[i]
            }
            contIterazioni = 0;
          }
        } 
      }
     window.requestAnimationFrame(galleryScroll)
     }

    window.requestAnimationFrame(galleryScroll);
body{
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    .container {
      width: 100vw;
      height: 100vh;
      display: flex;
      overflow: hidden;
    }
<div class="container">
      <img src="https://media-cdn.tripadvisor.com/media/photo-s/0e/eb/ad/3d/crazy-cat-cafe.jpg" alt="" class="image">
      <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Chairman_Meow_Bao.jpg/1200px-Chairman_Meow_Bao.jpg" alt="" class="image">
      <img src="https://static.scientificamerican.com/sciam/cache/file/92E141F8-36E4-4331-BB2EE42AC8674DD3_source.jpg" alt="" class="image">
      <img src="https://cdn.britannica.com/91/181391-050-1DA18304/cat-toes-paw-number-paws-tiger-tabby.jpg" alt="" class="image">
      <img src="https://www.orlandocatcafe.com/wp-content/uploads/2020/07/14.png" alt="" class="image">
      <img src="https://media-cdn.tripadvisor.com/media/photo-s/0e/eb/ad/3d/crazy-cat-cafe.jpg" alt="" class="image">
      <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Chairman_Meow_Bao.jpg/1200px-Chairman_Meow_Bao.jpg" alt="" class="image">
      <img src="https://static.scientificamerican.com/sciam/cache/file/92E141F8-36E4-4331-BB2EE42AC8674DD3_source.jpg" alt="" class="image">
    </div>

【讨论】:

  • 也许还使用requestAnimationFrame 而不是setInterval 以获得更流畅的动画效果
  • 我尝试将每个图像初始 width 保存在一个数组中,因此我可以将此数组中的值而不是固定值分配给图像,但我无法正确读取图像的宽度跨度>
  • requestAnimationFrame 已实现
  • 仅供参考:您对图像尺寸的想法很容易实现。您只需要在window.load 事件中获取图像宽度。喜欢window.addEventListener('load', [function to get image sizes here then start gallery], false);
  • 啊是的,我忘了它是这样工作的,谢谢我会实现这个
【解决方案2】:

在保持相同方法的同时,这应该与您提供的示例类似。如果您希望幻灯片保持其宽度,则所有图像都应具有相同的宽度。您的错误是没有将消失的图像添加回容器,最后并以其原始宽度,或者在这种情况下再次扩展。

// From https://www.javascripttutorial.net/javascript-queue/

function Queue() {
  this.elements = [];
}

Queue.prototype.enqueue = function(e) {
  this.elements.push(e);
};

Queue.prototype.dequeue = function() {
  return this.elements.shift();
};

Queue.prototype.isEmpty = function() {
  return this.elements.length == 0;
};

Queue.prototype.peek = function() {
  return !this.isEmpty() ? this.elements[0] : undefined;
};

Queue.prototype.length = function() {
  return this.elements.length;
}

let images = new Queue();

document.querySelectorAll(".image").forEach(img => images.enqueue(img));


var totImg = images.length();

var first = images.dequeue();
var last;

var firstWidth = first.width;
var lastWidth = 0.0;


var bounding = first.getBoundingClientRect();

var originalWidth = first.width;
var lastOriginalWidth = 0.0;

var step = 1.0;
var lastStep = 0.0;


function LoopFunction() {
  setInterval(galleryScroll, 20);
}

function galleryScroll() {
  bounding = first.getBoundingClientRect();

  if (bounding.left == 0) {
    //console.log("immagine bordo sx");
    if (first.width > 0) {
      firstWidth -= step;
      if (firstWidth < 0) {
        firstWidth = 0;
      }
      first.style.width = firstWidth + 'px';
    } else {
      if (last && last.width != lastOriginalWidth) {
        last.width = lastOriginalWidth;
      }

      let container = document.querySelector('.container');
      container.removeChild(first);

      last = first;
      lastOriginalWidth = originalWidth;
      last.width = 0.0;
      lastWidth = 0.0;

      images.enqueue(last);
      container.appendChild(last);

      first = images.dequeue();
      originalWidth = first.width;
      firstWidth = originalWidth;
      lastStep = step * lastOriginalWidth / originalWidth;
    }
  }

  if (last && last.width <= lastOriginalWidth) {
    lastWidth += lastStep;
    last.style.width = lastWidth + 'px';
    if (last.width > lastOriginalWidth) {
      last.width = lastOriginalWidth;
    }
  }
}

LoopFunction();
body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  width: 100vw;
  height: 100vh;
  display: flex;
  overflow: hidden;
}
<div class="container">
  <img src="https://media-cdn.tripadvisor.com/media/photo-s/0e/eb/ad/3d/crazy-cat-cafe.jpg" alt="" class="image">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Chairman_Meow_Bao.jpg/1200px-Chairman_Meow_Bao.jpg" alt="" class="image">
  <img src="https://static.scientificamerican.com/sciam/cache/file/92E141F8-36E4-4331-BB2EE42AC8674DD3_source.jpg" alt="" class="image">
  <img src="https://cdn.britannica.com/91/181391-050-1DA18304/cat-toes-paw-number-paws-tiger-tabby.jpg" alt="" class="image">
  <img src="https://www.orlandocatcafe.com/wp-content/uploads/2020/07/14.png" alt="" class="image">

</div>

更新 #1

根据 OP 的要求,以下是我所做更改的进一步说明:

  • 我使用 javascripttutorial 中的一个简单队列来获得反映目标的数据结构,即让图像从右到左移动,一旦它们“退出”场景,就可以再次入队。

  • 按照这个概念,我们需要同时指向队列头部和尾部的指针来更新它们的动画,特别是firstfirstWidthoriginalWidth分别用于指向头部,跟踪动画期间的当前宽度和重新进入场景时应返回的原始宽度。同样,last 也会发生这种情况。

  • 一旦完成,我们的循环将由两部分组成,一个处理头部动画并在头部图像到达width == 0 时移动队列中的图像,另一个处理尾部重新进入场景和扩大。为了实现连续性,我们在有了新队列后使用了一个简单的比例:step : originalWidth = lastStep : lastOriginalWidth,我们用它来确定每一帧的头部和尾部图像分别压缩和扩展多少。由于step 是一个固定参数,lastStep 很容易通过简单的算术计算得出:lastStep = step * lastOriginalWidth / originalWidth

  • 代码:

      let container = document.querySelector('.container');
      container.removeChild(first);

      last = first;
      lastOriginalWidth = originalWidth;
      last.width = 0.0;
      lastWidth = 0.0;

      images.enqueue(last);
      container.appendChild(last);

      first = images.dequeue();
      originalWidth = first.width;

只是更改队列的上下文切换,其中指针和相应数据得到更新以反映数据结构中的更改。所以first 变成了last,队列中的下一个图像是新压缩的first,因此originalWidth 移位。

让我们走得更远

现在,要实现您想要的实现,这仍然需要一些工作。首先,队列应该只反映实际的轮播,而不是您拥有的所有图像,因此理想情况下,您将拥有一个包含所有图像和相应元数据的第一个静态数据结构,在这种情况下,我们主要需要它们的原始宽度。那么您将有一个代表正在动画的图像的队列。

现在,您可以重复使用此代码并将图像的宽度更改为全部相等并适合您用于动画的视口,这应该可以正常工作。另一种选择是将头部图像的代码镜像到尾部,具有固定的步长,因此头部压缩step像素时,尾部扩展相同step像素,并且当尾部达到最大宽度时,在队列末尾引入一个新图像,成为新的尾部。

要更详细地了解最后一个,算法如下所示:

  • 用各自的宽度初始化图像容器的静态列表 (imgList);
  • 初始化一个空队列(imgQueue);
  • 初始化指向队列中最后一张图片的指针 (listPtr);
  • 获取轮播应填充的视口宽度 (vpWidth);
  • 虽然您还没有填充这样的宽度,但请继续将图像添加到队列中并将listPtr 移到imgList 左侧;
  • 具有与上述类似的循环功能,但if 上的last 复制了与first 类似的步骤;

这应该是一切,我会在一段时间内处理代码,现在我需要休息一下;)。

更新 #2

好的!制定了一些实际上工作得体的东西。如果您有任何建议,请告诉我,同时,这里是:

// From https://www.javascripttutorial.net/javascript-queue/
function Queue() {
  this.elements = [];
}
Queue.prototype.enqueue = function(e) {
  this.elements.push(e);
};
Queue.prototype.dequeue = function() {
  return this.elements.shift();
};
Queue.prototype.isEmpty = function() {
  return this.elements.length == 0;
};
Queue.prototype.first = function() {
  return !this.isEmpty() ? this.elements[0] : undefined;
};
Queue.prototype.length = function() {
  return this.elements.length;
};
// Added function to peek last element in queue
Queue.prototype.last = function() {
  return !this.isEmpty() ? this.elements[this.elements.length - 1] : undefined;
};
// Added function that pops the head, enqueues it and returns it
Queue.prototype.shift = function() {
  const head = this.dequeue();
  this.enqueue(head);
  return head;
}

// Returns a queue of HTMLElements based on the given class
function loadQueue(className) {
  const rQueue = new Queue();
  document.querySelectorAll('.' + className).forEach(image => rQueue.enqueue(image));
  return rQueue;
}

// Queue of images to be added to the animation queue
const imgQueue = loadQueue('image');
// Images being animated inside the div
const imgAnimQueue = new Queue();

// To limit calls to the carousel
const carousel = $('.container');

function init() {

  // Width of the viewport being used for the animation
  // TODO: this shoud update whenever the page is resized, or at least the div
  const vpWidth = carousel.width();

  // Keeps track of the width of the images added to the animation queue
  var currentFilledWidth = 0;

  // Now that we have loaded the static information, let's clear the div
  // that will be used as carousel, so that we can add the correct number
  // of images back in
  carousel.empty();

  // Filling the animation queue
  while (currentFilledWidth < vpWidth) {
    // In order not to change the static data, we clone the image HTMLElement
    const img = $(imgQueue.shift()).clone();
    // Enqueuing the new image in the animation queue
    imgAnimQueue.enqueue(img);
    // Adding this image into the carousel
    img.appendTo(carousel);
    currentFilledWidth = currentFilledWidth + img.width();

    // If we overflow the carousel width, we set the tail image to fill
    if (currentFilledWidth > vpWidth) {
      const overflow = currentFilledWidth - vpWidth;
      img.width(img.width() - overflow);
      currentFilledWidth -= overflow;
    }
  }

  const step = 1;
  var firstWidth = imgAnimQueue.first().width();
  var lastWidth = imgAnimQueue.last().width();

  // Now the loop can start
  window.requestAnimationFrame(animateCarousel);

  // Main function that animates the carousel
  function animateCarousel() {
    let first = imgAnimQueue.first();
    let last = imgAnimQueue.last();

    // If the image is still not disappeared, keep compressing it
    if (firstWidth > 0) {
      firstWidth -= step;
      if (firstWidth < 0) firstWidth = 0;
      first.width(firstWidth);
      // Otherwise, remove it from the carousel and update all the data structs
    } else {
      first.remove();
      imgAnimQueue.dequeue();

      if (imgAnimQueue.first() != last) {
        first = imgAnimQueue.first();
        firstWidth = first.width();
      } else {
        first = last;
        firstWidth = lastWidth;

        imgAnimQueue.enqueue($(imgQueue.shift()).clone());
        last = imgAnimQueue.last();
        lastWidth = last.width();
        last.appendTo(carousel);
      }
    }

    if (lastWidth <= last.attr("data-original-width")) {
      lastWidth += step;
      // The image has completely expanded, let's introduce the next one
      if (lastWidth >= last.attr("data-original-width")) {
        last.width(last.attr("data-original-width"));
        imgAnimQueue.enqueue($(imgQueue.shift()).clone());

        last = imgAnimQueue.last();
        last.width(0);
        lastWidth = last.width();
        last.appendTo(carousel);
        // Otherwise just keep expanding it
      } else {
        last.width(lastWidth);
      }
    }

    window.requestAnimationFrame(animateCarousel);
  }
}
body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  width: 100vw;
  height: 100vh;
  display: flex;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
  var loadedImages = 0;
  const count = 4;

  function loadImage(img) {
    if (loadedImages != count) {
      $(img).attr("data-original-width", $(img).width());
      loadedImages += 1;
      if (loadedImages == count) init();
    }
  }

</script>

<div class="container">
  <img onload="loadImage(this)" src="https://media-cdn.tripadvisor.com/media/photo-s/0e/eb/ad/3d/crazy-cat-cafe.jpg" alt="1" class="image">
  <img onload="loadImage(this)" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Chairman_Meow_Bao.jpg/1200px-Chairman_Meow_Bao.jpg" alt="2" class="image">
  <img onload="loadImage(this)" src="https://cdn.britannica.com/91/181391-050-1DA18304/cat-toes-paw-number-paws-tiger-tabby.jpg" alt="3" class="image">
  <img onload="loadImage(this)" src="https://www.orlandocatcafe.com/wp-content/uploads/2020/07/14.png" alt="4" class="image">
</div>

【讨论】:

  • 非常感谢您的示例!我试图了解如何使来自正确开始的 imgs 仅在它们进入视口而不是之前“扩展”。你能帮我理解怎么做吗?谢谢!
  • @baband 当然!我将更新我的答案以对代码进行更全面的解释。
猜你喜欢
  • 2020-05-24
  • 1970-01-01
  • 2020-04-29
  • 1970-01-01
  • 2014-11-06
  • 2012-10-16
  • 1970-01-01
  • 2014-01-03
相关资源
最近更新 更多