🇵🇭廃材アート×音楽 プロジェクト


<!doctype html>

<html lang="ja">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width,initial-scale=1" />

<title>現地の様子</title>

<style>

  /* レイアウト */

  .gallery {

    position: relative;

    max-width: 960px;      /* 横幅は必要に応じて変更 */

    margin: 0 auto;

    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  }

  /* 横スクロール(スワイプ対応/スナップ) */

  .slider {

    display: flex;

    overflow-x: auto;

    scroll-snap-type: x mandatory;

    -webkit-overflow-scrolling: touch;

    scroll-behavior: smooth;

    scrollbar-width: none;       /* Firefox */

  }

  .slider::-webkit-scrollbar { display: none; } /* Chrome/Safari */

  .slide {

    position: relative;

    flex: 0 0 100%;

    scroll-snap-align: start;

    aspect-ratio: 16 / 9;        /* 画像比率。必要なら変更 */

    background: #000;

  }

  .slide img {

    width: 100%;

    height: 100%;

    object-fit: cover;

    display: block;

  }


  /* ナビ(左右ボタン) */

  .nav {

    position: absolute;

    top: 50%;

    transform: translateY(-50%);

    border: 0;

    background: rgba(0,0,0,0.4);

    color: #fff;

    font-size: 28px;

    width: 44px;

    height: 44px;

    border-radius: 999px;

    cursor: pointer;

    line-height: 44px;

    text-align: center;

    padding: 0;

    user-select: none;

  }

  .nav:hover { background: rgba(0,0,0,0.55); }

  .prev { left: 8px; }

  .next { right: 8px; }


  /* ドット */

  .dots {

    display: flex;

    gap: 8px;

    justify-content: center;

    align-items: center;

    padding: 10px 0 4px;

  }

  .dot {

    width: 8px; height: 8px; border-radius: 50%;

    background: #c8c8c8;

  }

  .dot.active { background: #333; }


  /* 画像の上にキャプションを出したい場合(任意) */

  .cap {

    position: absolute;

    left: 0; right: 0; bottom: 0;

    padding: 8px 12px;

    color: #fff;

    font-size: 14px;

    background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 80%);

  }

</style>

</head>

<body>


<div class="gallery" aria-label="フォトギャラリー">

  <div class="slider" id="slider" aria-live="polite">

    <!-- ★ここをあなたの画像に差し替え -->

    <figure class="slide"><img src="image1.jpg" alt="写真1"><figcaption class="cap">写真1のキャプション</figcaption></figure>

    <figure class="slide"><img src="image2.jpg" alt="写真2"><figcaption class="cap">写真2のキャプション</figcaption></figure>

    <figure class="slide"><img src="image3.jpg" alt="写真3"><figcaption class="cap">写真3のキャプション</figcaption></figure>

  </div>


  <!-- 左右ナビ -->

  <button class="nav prev" type="button" aria-label="前へ">‹</button>

  <button class="nav next" type="button" aria-label="次へ">›</button>


  <!-- ドット -->

  <div class="dots" id="dots" aria-hidden="true"></div>

</div>


<script>

  (function () {

    const slider = document.getElementById('slider');

    const slides = Array.from(slider.querySelectorAll('.slide'));

    const prevBtn = document.querySelector('.prev');

    const nextBtn = document.querySelector('.next');

    const dotsEl = document.getElementById('dots');


    let index = 0;

    let autoTimer = null;

    const AUTO_MS = 4000; // 自動スライド間隔


    // ドット生成

    slides.forEach((_, i) => {

      const d = document.createElement('span');

      d.className = 'dot' + (i === 0 ? ' active' : '');

      dotsEl.appendChild(d);

    });

    const dots = Array.from(dotsEl.children);


    function goTo(i, smooth = true) {

      index = (i + slides.length) % slides.length;

      const left = slides[index].offsetLeft;

      slider.scrollTo({ left, behavior: smooth ? 'smooth' : 'auto' });

      dots.forEach((d, di) => d.classList.toggle('active', di === index));

    }


    function next() { goTo(index + 1); }

    function prev() { goTo(index - 1); }


    function startAuto() {

      stopAuto();

      autoTimer = setInterval(next, AUTO_MS);

    }

    function stopAuto() {

      if (autoTimer) clearInterval(autoTimer);

      autoTimer = null;

    }


    // スクロール終了後に現在位置を確定(手動スワイプ対応)

    let scrollTimeout;

    slider.addEventListener('scroll', () => {

      stopAuto();

      if (scrollTimeout) clearTimeout(scrollTimeout);

      scrollTimeout = setTimeout(() => {

        // もっとも近いスライドを計算

        let nearest = 0, min = Infinity;

        slides.forEach((s, i) => {

          const dist = Math.abs(slider.scrollLeft - s.offsetLeft);

          if (dist < min) { min = dist; nearest = i; }

        });

        goTo(nearest, false);

        startAuto();

      }, 120);

    });


    // ボタン

    nextBtn.addEventListener('click', () => { stopAuto(); next(); startAuto(); });

    prevBtn.addEventListener('click', () => { stopAuto(); prev(); startAuto(); });


    // 初期化

    goTo(0, false);

    startAuto();


    // 画像読み込み後に位置を再調整(レイアウトずれ対策)

    window.addEventListener('load', () => goTo(index, false));

    window.addEventListener('resize', () => goTo(index, false));

  })();

</script>


</body>

</html>

ご協力ありがとうございました。

2025.8.1〜8.31 クラウドファンディングを実施。

クラウドファンディングページはこちら





ページを報告する

コピーしました

有料会員になるとオーナーが配信している有料会員限定のコンテンツを閲覧できるようになります。

クレメニアン ¥960 / 月

会員登録する

コピーしました