如何使用酷CS和JS功能制作简单的幻灯片
#javascript #css #jsproxy #cssvariables

你好。今天,我使用酷CS和JS功能进行简单的幻灯片。
我将使用:

  • CSS变量
  • CSS转换
  • CSS功能
  • CSS网格
  • JS代理

HTML

<div class="slideshow">
  <div class="slides-container">
    <div class="slides">
      <img class="slide" src="https://picsum.photos/600/300?v=1" alt="">
      <img class="slide" src="https://picsum.photos/600/300?v=2" alt="">
      <img class="slide" src="https://picsum.photos/600/300?v=3" alt="">
      <img class="slide" src="https://picsum.photos/600/300?v=4" alt="">
      <img class="slide" src="https://picsum.photos/600/300?v=5" alt="">
    </div>
  </div>
  <div class="bullets">
  </div>
  <div class="buttons">
    <button type="button" class="prev">
      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
        <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
      </svg>

    </button>
    <button type="button" class="next">
      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
        <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
      </svg>
    </button>
  </div>
</div>

让我们分解。

我们有一个div,其中包含一类.slideshow的整个幻灯片。这个容器有3个直接的孩子。第一个是.slides-container,它将充当我们幻灯片将显示的“窗口”。然后是一个空的.bullets容器,我们将根据幻灯片的数量动态放置子弹。最后一个是.buttons容器,带有.prev.next按钮。

.slides-container内部有另一个带有一类.slides的容器,可容纳我们所有的幻灯片。该DIV将在我们的“窗口”后方向右移动,以揭示活动幻灯片。

对于图像,我使用了https://picsum.photos的图片,对于.prev.next按钮,我使用了https://heroicons.com的一些图标。

CSS

首先,让我们定义一些CSS变量,这些变量将稍后使用。

:root {
  --max-size: 600px;  
  --size: min(var(--max-size), 100vw);
  --active: 0;
}

--max-size是每张幻灯片的最大尺寸。
--size是整个幻灯片的计算大小。它的屏幕比--max-size小100VW,而--max-size对于较大的屏幕。
--active变量保存了活动幻灯片的索引。

现在,外部容器将是带有1列和2行的CSS网格。第一行将包含幻灯片和按钮,第二行将包含子弹。

.slideshow {
  display: grid;
  grid-template-rows: 1fr auto;
  grid-template-columns: 1fr;
  gap: 10px;
  width: var(--size);
}

然后,我们的“窗口”。 .slides-container将位于父格的第1列和第1列中。它将通过具有透明背景并隐藏任何溢出的东西来创建一个孔(窗口)。

.slides-container {
  grid-column: 1;
  grid-row: 1;
  overflow: hidden;
}

然后是.slides。这是一个包含我们所有幻灯片的Div。它具有display: flex,可以连续将所有内容放入所有内容,并将四处移动以揭示活动幻灯片。这里有趣的部分是基于活动幻灯片和幻灯片大小的容器位置的计算。
-1 * size * active。假设大小为600px。当活动幻灯片是0时,它将为我们在X轴上给我们0移动。当活动是第一个时,它将给予-1 * 600px * 1 = -600px,因此它将向左移动600px。

.slides {
  display: flex;
  transform: translateX(calc(-1 * var(--size) * var(--active)));
  transition: transform 0.3s ease;
}

我们的图像需要具有等于--size变量的宽度,才能得到响应。这是因为他们的父母真的很长,我们不能依靠图像占用所有可用宽度并具有自动的高度。

img {
  width: var(--size);
}

然后是按钮容器。这个Div必须坐在幻灯片的顶部。我不喜欢使用position: absolute,因此我使用了将多个元素放在同一网格单元格中的网格功能。因此,我将其放在与.slides-container的网格单元中。问题是我无法单击幻灯片。如果幻灯片具有链接,我将无法单击它,因为另一个DIV在其前面。为了解决这个问题,我在按钮容器中添加了pointer-events: none。现在我无法单击按钮。所以我在按钮上添加pointer-events: all
该容器中的按钮将在垂直方向和水平侧面。

.buttons {
  z-index: 1;
  display: flex;
  justify-content: space-between; /* Horizontally at the sides */
  align-items: center; /* Vertically in the middle */
  padding: 10px;
  grid-column: 1;
  grid-row: 1;
  pointer-events: none;
}

我认为在此处包括按钮样式并不重要。稍后我有一个Codepen来展示幻灯片演示。

.bullets容器位于幻灯片网格的第2行中。它与中心的子弹对齐。

.bullets {
  grid-column: 1;
  grid-row: 2;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 12px;
}

每个子弹都是圆形的,与活动幻灯片相对应的一个。

到目前为止,我们有这个。它尚不正常,但风格。

JavaScript

首先,让我们在变量中保留一些有用的东西。

// We keep references to all needed elements.
const slideshow = document.querySelector(".slideshow");
const slidesContainer = slideshow.querySelector(".slides");
const slides = slideshow.querySelectorAll(".slide");
const bulletContainer = slideshow.querySelector(".bullets");
const prev = slideshow.querySelector(".prev");
const next = slideshow.querySelector(".next");
const length = slides.length;

然后,让我们根据幻灯片计数创建子弹。我在这里使用文档fragmet将新元素插入DOM。

// Automatically create the bullets based on
// the amount of slides we have.
const fragment = document.createDocumentFragment();
slides.forEach((slide, index) => {
  const bullet = document.createElement("span");
  bullet.classList.add("bullet");
  if (!index) {
    // Set the first bullet as active
    bullet.classList.add("active");
  }
  // Add the click functionality to the bullet
  bullet.addEventListener("click", () => active.slide = index);
  fragment.appendChild(bullet);
});
bulletContainer.appendChild(fragment);

// Keep a reference to the bullets
// so we can add/remove the .active class.
const bullets = bulletContainer.querySelectorAll(".bullet");

现在,让我们在按钮中添加功能。单击.next按钮时,我们要将active索引增加1,然后单击.prev按钮时,我们希望将active索引减少1.
当我们到达最后一个幻灯片并单击下一步时,它应该返回第一个。当我们处于首先并单击之前,它应该转到最后一个。但是,我真的不喜欢将此逻辑放入点击处理程序中,因为通过单击.next,我不在乎哪个是下一个。请告诉我下一个。因此,我使用了JavaScript代理的魔力。您可能已经注意到,在子弹单击侦听器中,我使用active.slide设置了活动幻灯片索引。这就是它的发展。

// We use Proxy to include some kind of reactivity.
// By just changing active.slide, the DOM gets
// changed automatically, without the need to
// make the DOM changes within the event handlers.
const active = new Proxy(
  { slide: 0 },
  {
    set(obj, prop, value) {
      if (prop == "slide") {
        // If we click "prev" while on the first slide
        // then go to the last one.
        if (value < 0) {
          value = length - 1;
        }

        // If we click "next" while on the last slide
        // then go to the first one.
        if (value >= length) {
          value = 0;
        }

        // Make the appropriate DOM changes
        slidesContainer.style.setProperty("--slide", value);
        bullets[obj[prop]].classList.remove("active");
        bullets[value].classList.add("active");
      }

      // Actually set the active.slide prop to it's new value
      obj[prop] = value;
      return true;
    }
  }
);

MDN网站

所述

代理对象允许您创建一个可以代替原始对象使用的对象,但可以重新定义基本对象操作,例如get,设置和定义属性。

我将代理作为目标对象作为第一个参数,而处理程序则是第二个参数。在处理程序中有一个设置器函数。
第一个参数obj是目标对象{ slide: 0 }。第二个参数prop是我们正在设置的属性,在我们的情况下,slide。第三个参数是我们要设置为属性的值。
进入六个,我将所有用于计算哪个幻灯片是应显示的幻灯片的逻辑,并进行适当的DOM更改。我们可以看到,必须完成的唯一DOM更改是设置--active CSS变量适当的活动幻灯片索引,并在相应的子弹上切换.active类。

在JavaScript代理的帮助下,我能够分开关注点,并在.prev.next按钮上添加非常简单的单击处理程序。

// Add the prev/next functionality to the buttons
prev.addEventListener("click", () => active.slide--);
next.addEventListener("click", () => active.slide++);

这是完整的编码器。

最后,这是另一个编码。它与最后一个相同,但是如果按空格,它将显示为3D以显示元素如何彼此放置,以及“窗口”如何工作。

我希望您喜欢我用来制作此幻灯片的技术。我很想听听更好的选择。我一直在学习新的东西。

感谢您阅读此远!