ヒーロー カルーセル

設計

  • 写真数の増減に対応してスライダー数も対応
  • 最終画面で3秒後にコピーを表示、さらに7秒後にコピーを消去して、アニメーションも停止
  • レスポンシブでレイアウトを変える
最終コピー
表示エリア
NO
顧客
address
Data
注意

WordPressは、cssネストが複雑になると「コードを編集」のプレビューでは表示されていても公開画面、テストプレビューで表示されなくなるので
cssネストを最低限にする

HTML

<div class="slider-container">
  <div class="slides">
    <div class="slide"></div>
    <div class="slide"></div>
    <div class="slide"></div>
    <div class="slide"></div>
    <div class="slide"></div>
    <div class="slide copy">最終コピー<br />表示エリア</div>
  </div>
  <div class="slider-nav">
    <button class="nav-btn prev-btn"></button>
    <button class="nav-btn next-btn"></button>
    <div class="dots-nav"></div>
  </div>
  <div class="slides_info">
    <div class="logo">
      <h1>h1 hide</h1>
    </div>
    <div class="case">NO</div>
    <div class="name">顧客</div>
    <div class="add">address</div>
    <div class="data data-num">Data</div>
  </div>
</div>

JavaScript

<script>
document.addEventListener("DOMContentLoaded", () => {
  const photoSlides = document.querySelectorAll(".slide:not(.copy)");
  const copySlide = document.querySelector(".slide.copy");
  const dotsNav = document.querySelector(".dots-nav");
  const prevBtn = document.querySelector(".prev-btn");
  const nextBtn = document.querySelector(".next-btn");
  if (photoSlides.length === 0) return;

  let currentIndex = 0;
  let slideTimeout;
  let copyTimeout;
  const photoIntervalTime = 3000; // 写真の遷移間隔3秒

  photoSlides.forEach((_, index) => {
    const dot = document.createElement("div");
    dot.classList.add("dot");
    if (index === 0) dot.classList.add("active");
    dot.addEventListener("click", () => {
      goToSlide(index);
    });
    dotsNav.appendChild(dot);
  });

  const dots = document.querySelectorAll(".dot");
  function goToSlide(index, isAuto = false) {
    clearAllTimers();

    photoSlides[currentIndex].classList.remove("active");
    dots[currentIndex].classList.remove("active");
    if (copySlide) copySlide.classList.remove("active");
    if (index >= photoSlides.length) {
      currentIndex = 0;
    } else if (index < 0) {
      currentIndex = photoSlides.length - 1;
    } else {
      currentIndex = index;
    }

    photoSlides[currentIndex].classList.add("active");
    dots[currentIndex].classList.add("active");

    if (currentIndex === photoSlides.length - 1) {
      if (copySlide) {
        // 3秒後に .copy を表示
        copyTimeout = setTimeout(() => {
          copySlide.classList.add("active");
          // +7秒後に .copy を消去して、アニメーションを停止
          copyTimeout = setTimeout(() => {
            copySlide.classList.remove("active");
          }, 7000);
        }, 1000);
      }
    } else {
      slideTimeout = setTimeout(() => {
        goToSlide(currentIndex + 1, true);
      }, photoIntervalTime);
    }
  }

  function clearAllTimers() {
    clearTimeout(slideTimeout);
    clearTimeout(copyTimeout);
  }

  prevBtn.addEventListener("click", () => goToSlide(currentIndex - 1));
  nextBtn.addEventListener("click", () => goToSlide(currentIndex + 1));

  photoSlides[0].classList.add("active");
  slideTimeout = setTimeout(() => {
    goToSlide(currentIndex + 1, true);
  }, photoIntervalTime);
});
</script>

CSS

.slider-container {
  background-color: #fff;

  @media (737px < width) {
    position: relative;
    width: 100%;
    aspect-ratio: 1.774;
    object-fit: contain;
    overflow: hidden;
    max-height: 100dvh;
  }

  & .slides {
    width: 100%;
    aspect-ratio: 1.774;
    object-fit: contain;
  }

  & .slide {
    position: absolute;
    color: #fff;
    top: 0;
    left: 0;
    width: 100%;
    opacity: 0;
    z-index: 1;
    transition: opacity 0.8s ease-in-out;
    aspect-ratio: 1.774;
    object-fit: contain;

    @media (737px < width) {
      height: 100%;
    }
    &.active {
      opacity: 1;
      z-index: 2;
    }
  }

  & .slide.copy {
    z-index: 3;
    display: grid;
    place-content: center;
    text-align: center;
    font-weight: 700;
    pointer-events: none;
    transition: all 1s;
    line-height: 3;

    @media (737px < width) {
      font-size: calc(1.96em + ((1vw - 7.67px) * 1.9));
    }
    @media (736px >= width) {
      font-size: clamp(18px, 5.600007vw, 25px);
      line-height: 1.6;
      letter-spacing: 0.05em;
      aspect-ratio: 1.774;
      object-fit: contain;
      line-height: 4;
    }

    &.active {
      z-index: 2;
      animation-name: slide_copies;
      animation-duration: 7.2s;
    }
  }

  & .nav-btn {
    z-index: 10;
    color: #fff;
    border: none;
    cursor: pointer;
    background-color: rgba(0, 0, 0, 0.5);
    @media (737px < width) {
      transition: background-color 0.5s;
    }
    &:after {
      content: "";
      display: inline-block;
      background-color: #fff;
      z-index: 3;
      transition:
        background-color 0.3s ease-in-out,
        translate 0.3s ease-in-out;
    }
    @media (737px < width) {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      padding: 10px 1em;
      &:after {
        height: 16px;
        width: 12px;
      }
      @media (any-hover: hover) {
        &:hover {
          background-color: rgba(0, 28, 209, 0.8);
        }
      }
    }
    @media (736px >= width) {
      padding: 0.5em 1em;
      &:after {
        height: 12px;
        width: 8px;
      }
    }
  }
  & .slider-nav {
    @media (736px >= width) {
      display: flex;
      justify-content: space-between;
      align-items: center;
      background-color: #000;
      & .prev-btn {
        order: 1;
      }
      & .next-btn {
        order: 3;
      }
      & .dots-nav {
        order: 2;
        flex-wrap: wrap;
        justify-content: center;
      }
    }
  }

  & .prev-btn {
    @media (737px < width) {
      left: 0;
    }

    &:after {
      clip-path: polygon(0 50%, 100% 0, 100% 100%);
      translate: -2px 0;
      @media (736px >= width) {
        translate: -2px 2px;
      }
    }
    @media (any-hover: hover) {
      &:hover {
        &:after {
          translate: -6px 0;
        }
      }
    }
  }
  & .next-btn {
    @media (737px < width) {
      right: 0;
    }
    &:after {
      clip-path: polygon(0 0, 100% 50%, 0 100%);
      translate: 2px 0;
      @media (736px >= width) {
        translate: 2px 2px;
      }
    }
    @media (any-hover: hover) {
      &:hover {
        &:after {
          translate: 6px 0;
        }
      }
    }
  }
  & .dots-nav {
    z-index: 7;
    display: flex;
    @media (737px < width) {
      position: absolute;
      bottom: 15px;
      left: 50%;
      transform: translateX(-50%);
      gap: 10px;
    }
    @media (736px >= width) {
      gap: 5px;
    }
    & .dot {
      background-color: rgba(255, 255, 255, 0.5);
      cursor: pointer;
      @media (737px < width) {
        width: 24px;
        height: 7px;
        border-radius: 10vw;
        transition: background-color 0.3s;
      }
      @media (736px >= width) {
        width: 20px;
        height: 5px;
        border-radius: 0;
      }
      &:hover {
        background-color: rgba(0, 28, 209, 0.5);
      }
      &.active {
        background-color: #ffffff;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
        pointer-events: none;
      }
      @media (736px >= width) {
        background-color: #595a69;
      }
    }
  }
  & .slides_info {
    z-index: 5;
    color: #fff;
    letter-spacing: 0.06em;

    @media (737px < width) {
      position: absolute;
      padding: 1em;
      bottom: 1.3em;
      left: 1em;
      display: grid;
      grid-template-columns: repeat(2, auto);
      grid-template-rows: repeat(3, auto);
      grid-column-gap: 20px;
    }
    @media (736px >= width) {
      position: relative;
      text-align: center;
      padding-bottom: 1rem;
      &::before,
      &::after {
        position: absolute;
        content: "";
        display: inline-block;
        border-bottom: solid 1px #000;
        top: -1px;
        width: 2em;
      }
      &::before {
        left: 0;
      }
      &::after {
        right: 0;
      }
    }

    & .case {
      color: #111;
      background-color: #ffffffaa;
      display: flex;
      justify-content: center;
      align-items: center;
      filter: invert();
      backdrop-filter: blur(5px);
      grid-area: 1 / 1 / 4 / 2;
      @media (737px < width) {
        padding: 1rem;
        font-size: 22px;
      }
      &::before {
        content: "Photo";
        text-align: center;
        font-size: 13px;
        margin: 8px 6px 0 0;
      }
      @media (736px >= width) {
        font-size: clamp(15.48px, 4.800006vw, 21.6px);
        padding: 0 0 0.5rem;
        &::before {
          font-size: 12px;
          margin: 5px 5px 0 0;
        }
      }
    }
    & .name {
      line-height: 1.2;
      font-weight: 900;
      opacity: 1;
      translate: 0 0;
      transition:
        opacity 0.6s ease-in-out,
        translate 0.6s ease-in-out;
      @media (737px < width) {
        font-size: 24px;
        border-bottom: 2px solid rgba(230, 109, 38, 1);
        padding: 0 5px 6px 0px;
        text-shadow: 0px 0px 5px #000;
      }
      grid-area: 1 / 2 / 2 / 3;
      &::after {
        content: "\69D8";
        margin-left: 6px;
        font-size: 0.8em;
      }
      @media (736px >= width) {
        color: #000;
        font-size: clamp(19.78px, 6.133341vw, 27.6px);
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 1rem 1rem 0;
      }
      @starting-style {
        opacity: 0;
        translate: 0 10px;
      }
    }
    & .add {
      opacity: 0.9;
      translate: 0 0;
      transition:
        opacity 0.6s ease-in-out 0.5s,
        translate 0.6s ease-in-out 0.5s;
      @media (737px < width) {
        font-size: 13px;
        padding: 4px 0 5px 2px;
        text-shadow: 0px 0px 3px #000;
      }

      grid-area: 2 / 2 / 3 / 3;
      @media (736px >= width) {
        font-size: clamp(10.32px, 3.200004vw, 14.4px);
        color: #444;
        margin-top: 0.1em;
      }
      @starting-style {
        opacity: 0;
        translate: 0 8px;
      }
    }
    & .data {
      line-height: 0;
      opacity: 1;
      display: flex;
      align-items: baseline;
      translate: 0 0;
      transition:
        opacity 0.6s ease-in-out 1s,
        translate 0.6s ease-in-out 1s;
      @media (737px < width) {
        text-shadow: 0px 0px 5px #000;
        margin-top: 0.2em;
        grid-area: 3 / 2 / 4 / 3;
      }
      @media (736px >= width) {
        margin-top: 0.7em;
        color: #000;
        justify-content: center;
      }
      @starting-style {
        opacity: 0;
        translate: 0 10px;
      }

      & .large-num {
        font-size: 1.5em;
        margin-inline: 0.15em;
        translate: 0 1px;
        @media (736px >= width) {
          font-size: 1.25em;
        }
      }

      &::before {
        content: "info";
        color: #fff;
        margin-right: 10px;
        font-size: 13px;
        line-height: 1.5;
        padding: 0 5px 0 8px;
        background: rgba(34, 67, 216, 0.8);
        text-shadow: none;
        display: inline-flex;
        position: relative;
        top: -1px;
        left: 0;
        @media (736px >= width) {
          font-size: 12px;
        }
      }
    }
    & h1 {
      display: none;
    }
  }
}
@keyframes slide_copies {
  0% {
    translate: 0 0.5em;
    opacity: 0;
  }
  15% {
    translate: 0 0;
    opacity: 1;
  }
  85% {
    translate: 0 0;
    opacity: 1;
  }
  100% {
    translate: 0 -0.5em;
    opacity: 0;
  }
}

CSS

前の記事

pure cssで作るタブメニュー