Bloggerに関するカスタマイズ・テンプレート・TIPSを紹介しています。

2026/02/28

Bloggerデザイン : ダイナミック・カードスライダー

1. デザインのタイプ

【ダイナミック・カードスライダー(タイムライン同期型)】
背景画像とプレビューカードが連動し、滑らかなトランジションで切り替わる、没入感の高いモダンなスライダーデザイン。中央の軸から左右へ交互に展開されるアニメーションが、情報の「流れ」をドラマチックに演出します。

2. デザインの特徴

  • フルスクリーン・イマージョン
    コンテナいっぱいに広がる背景画像で視覚的な没入感を最大化。
  • シームレス・トランジション
    GSAPを使用した高度なアニメーションにより、情報の切り替えを劇的に演出します。
  • インタラクティブ・フィードバック
    操作ボタンにホバー・アクティブ時のリアクションを実装し、直感的な操作感を実現。

3. 紹介コード

HTML・CSS JavaScript

<style>
/* --- 余白リセット --- */
#slider-container, #slider-container * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

#slider-container {
    margin: 2em auto;
    background-color: #000;
    color: #FFFFFFDD;
    position: relative;
    overflow: hidden;
    font-family: "Inter", sans-serif;
    width: 100%;
    max-width: 900px;
    height: 400px;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}

#slider-container .card {
    position: absolute;
    left: 0;
    top: 0 !important;
    width: 100%;
    height: 400px;
    background-position: center;
    background-size: cover;
    z-index: 10;
}

#slider-container .card-content {
    position: absolute;
    z-index: 40;
    color: #FFFFFFDD;
    padding-left: 8px; 
    padding-right: 4px;
    width: 100%;
    pointer-events: none;
}

#slider-container .content-place { margin-top: 2px; font-size: 0.75rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#slider-container .content-title-1, #slider-container .content-title-2 { font-weight: 600; font-size: 0.95rem; font-family: "Oswald", sans-serif; line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#slider-container .content-start { width: 20px; height: 2px; border-radius: 99px; background-color: #FFFFFFDD; margin-top: 4px; }

#slider-container .details {
    z-index: 80 !important;
    position: absolute;
    top: 60px;
    left: 40px;
    max-width: 350px;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
}

#slider-container .details > .cta { pointer-events: auto; }
#slider-container .details .title-1, #slider-container .details .title-2 { font-weight: 600; font-size: 36px; font-family: "Oswald", sans-serif; line-height: 1.1; }
#slider-container .details .place-box { height: 30px; overflow: hidden; }
#slider-container .details .place-box .text { padding-top: 10px; font-size: 14px; position: relative; }
#slider-container .details .place-box .text:before { top: 0; left: 0; position: absolute; content: ""; width: 20px; height: 3px; border-radius: 99px; background-color: white; }
#slider-container .details .title-box-1, #slider-container .details .title-box-2 { margin-top: 2px; height: 45px; overflow: hidden; }
#slider-container .details > .desc { margin-top: 10px; font-size: 13px; line-height: 1.5; color: #ccc; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
#slider-container .details > .cta { margin-top: 15px; display: flex; align-items: center; }
#slider-container .details > .cta > .bookmark { border: none; background-color: #ecad29; width: 30px; height: 30px; border-radius: 99px; color: white; display: grid; place-items: center; cursor: pointer; transition: transform 0.2s; }
#slider-container .details > .cta > .bookmark:active { transform: scale(0.9); }
#slider-container .details > .cta > .discover { border: 1px solid #ffffff; background-color: transparent; height: 30px; border-radius: 99px; color: #ffffff; padding: 0 16px; font-size: 11px; margin-left: 10px; text-transform: uppercase; cursor: pointer; transition: background 0.2s; }
#slider-container .details > .cta > .discover:hover { background: #ffffff22; }

#slider-container .pagination {
    position: absolute;
    z-index: 100;
    bottom: 20px;
    left: 40px;
    display: flex;
    align-items: center;
    gap: 15px;
}

#slider-container .pagination > .arrow { 
    width: 36px; 
    height: 36px; 
    border-radius: 999px; 
    border: 1px solid #ffffff55; 
    display: grid; 
    place-items: center; 
    cursor: pointer; 
    pointer-events: auto; 
    transition: all 0.2s ease; 
    background-color: transparent;
}
#slider-container .pagination > .arrow:hover { background-color: rgba(255, 255, 255, 0.2); border-color: #ffffff; }
#slider-container .pagination > .arrow:active { background-color: rgba(255, 255, 255, 0.4); transform: scale(0.9); }
#slider-container .pagination > .arrow svg { width: 18px; height: 18px; color: #ffffff; transition: color 0.2s; }

#slider-container .pagination .progress-sub-container { width: 120px; height: 36px; display: flex; align-items: center; }
#slider-container .pagination .progress-sub-background { width: 100%; height: 2px; background-color: #ffffff33; }
#slider-container .pagination .progress-sub-foreground { height: 2px; background-color: #ecad29; width: 0%; }
#slider-container .slide-numbers { width: 30px; height: 36px; overflow: hidden; position: relative; }
#slider-container .slide-numbers .item { width: 30px; height: 36px; position: absolute; color: white; display: grid; place-items: center; font-size: 20px; font-weight: bold; }

#slider-container .cover { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #fff; z-index: 110; }
</style>

<div id="slider-container">
    <div id="demo"></div>
    <div class="details" id="details-even">
        <div class="place-box"><div class="text"></div></div>
        <div class="title-box-1"><div class="title-1"></div></div>
        <div class="title-box-2"><div class="title-2"></div></div>
        <div class="desc"></div>
        <div class="cta">
            <button class="bookmark"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.32 2.577a49.255 49.255 0 0111.36 0c1.497.174 2.57 1.46 2.57 2.93V21l-7.165-3.583L4.835 21V5.507c0-1.47 1.073-2.756 2.57-2.93z" /></svg></button>
            <button class="discover">Discover</button>
        </div>
    </div>
    <div class="details" id="details-odd">
        <div class="place-box"><div class="text"></div></div>
        <div class="title-box-1"><div class="title-1"></div></div>
        <div class="title-box-2"><div class="title-2"></div></div>
        <div class="desc"></div>
        <div class="cta">
            <button class="bookmark"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.32 2.577a49.255 49.255 0 0111.36 0c1.497.174 2.57 1.46 2.57 2.93V21l-7.165-3.583L4.835 21V5.507c0-1.47 1.073-2.756 2.57-2.93z" /></svg></button>
            <button class="discover">Discover</button>
        </div>
    </div>
    <div class="pagination">
        <div id="prev" class="arrow"><svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.75 19.5L8.25 12l7.5-7.5" /></svg></div>
        <div id="next" class="arrow"><svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.25 4.5l7.5 7.5-7.5 7.5" /></svg></div>
        <div class="progress-sub-container">
            <div class="progress-sub-background"><div class="progress-sub-foreground"></div></div>
        </div>
        <div class="slide-numbers" id="slide-numbers"></div>
    </div>
    <div class="cover"></div>
</div>
  

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
(function() {
    const data = [
        { place:'Switzerland Alps', title:'SAINT', title2:'ANTONIEN', description:'Tucked away in the Switzerland Alps, Saint Antönien offers an idyllic retreat.', image:'https://assets.codepen.io/3685267/timed-cards-1.jpg' },
        { place:'Japan Alps', title:'NAGANO', title2:'PREFECTURE', description:'Nagano Prefecture, set within the majestic Japan Alps, is cultural treasure.', image:'https://assets.codepen.io/3685267/timed-cards-2.jpg' },
        { place:'Sahara Desert', title:'MARRAKECH', title2:'MEROUGA', description:'The journey from the vibrant souks of Marrakech to the tranquil sands.', image:'https://assets.codepen.io/3685267/timed-cards-3.jpg' },
        { place:'Sierra Nevada', title:'YOSEMITE', title2:'NATIONAL PARK', description:'Yosemite National Park is a showcase of the American wilderness.', image:'https://assets.codepen.io/3685267/timed-cards-4.jpg' },
        { place:'Tarifa - Spain', title:'LOS LANCES', title2:'BEACH', description:'Los Lances Beach in Tarifa is a coastal paradise known for winds.', image:'https://assets.codepen.io/3685267/timed-cards-5.jpg' },
        { place:'Cappadocia', title:'GÖREME', title2:'VALLEY', description:'Göreme Valley in Cappadocia is a historical marvel with unique geology.', image:'https://assets.codepen.io/3685267/timed-cards-6.jpg' }
    ];
    const _ = (id) => document.getElementById(id);
    const container = _('slider-container');
    const demo = _('demo');
    let order = [0, 1, 2, 3, 4, 5];
    let detailsEven = true;
    let isAnimating = false;
    let autoPlayTimeout;
    const cardWidth = 120;
    const cardHeight = 180;
    const gap = 20;
    const numberSize = 30;
    const ease = "sine.inOut";

    demo.innerHTML = data.map((i, index) => `<div class="card" id="card${index}" style="background-image:url(${i.image})"></div>`).join('') +
                     data.map((i, index) => `<div class="card-content" id="card-content-${index}"><div class="content-start"></div><div class="content-place">${i.place}</div><div class="content-title-1">${i.title}</div><div class="content-title-2">${i.title2}</div></div>`).join('');
    _('slide-numbers').innerHTML = data.map((_, index) => `<div class="item" id="slide-item-${index}">${index + 1}</div>`).join('');

    function init() {
        const [active, ...rest] = order;
        const detailsActive = detailsEven ? "#details-even" : "#details-odd";
        const detailsInactive = detailsEven ? "#details-odd" : "#details-even";
        const width = container.offsetWidth;
        const height = container.offsetHeight;
        const offsetTop = height - 210;
        const offsetLeft = width - 450;
        gsap.set(detailsInactive, { opacity: 0, visibility: 'hidden', zIndex: 12 });
        updateDetails(detailsActive, active);
        gsap.set(`#card${active}`, { x: 0, y: 0, width: width, height: height, zIndex: 10 });
        gsap.set(`#card-content-${active}`, { opacity: 0 });
        gsap.set(detailsActive, { opacity: 0, x: -50, zIndex: 80, visibility: 'visible' });
        rest.forEach((i, index) => {
            gsap.set(`#card${i}`, { x: offsetLeft + index * (cardWidth + gap), y: offsetTop, width: cardWidth, height: cardHeight, zIndex: 30, borderRadius: 8 });
            gsap.set(`#card-content-${i}`, { x: offsetLeft + index * (cardWidth + gap), y: offsetTop + cardHeight - 95, zIndex: 40 });
            gsap.set(`#slide-item-${i}`, { x: (index + 1) * numberSize });
        });
        gsap.to(".cover", { x: width + 100, duration: 0.8, ease, onComplete: () => { startAutoPlay(); } });
        gsap.to(detailsActive, { opacity: 1, x: 0, ease, delay: 0.4 });
        _('next').addEventListener('click', () => handleNav(1));
        _('prev').addEventListener('click', () => handleNav(-1));
    }

    function updateDetails(id, index) {
        const item = data[index];
        const el = document.querySelector(id);
        el.querySelector('.text').textContent = item.place;
        el.querySelector('.title-1').textContent = item.title;
        el.querySelector('.title-2').textContent = item.title2;
        el.querySelector('.desc').textContent = item.description;
    }

    async function step(direction = 1) {
        if (isAnimating) return;
        isAnimating = true;
        if (direction === 1) { order.push(order.shift()); } else { order.unshift(order.pop()); }
        detailsEven = !detailsEven;
        const detailsActive = detailsEven ? "#details-even" : "#details-odd";
        const detailsInactive = detailsEven ? "#details-odd" : "#details-even";
        const [active, ...rest] = order;
        updateDetails(detailsActive, active);
        gsap.set(detailsActive, { zIndex: 80, opacity: 0, visibility: 'visible' });
        gsap.to(detailsActive, { opacity: 1, delay: 0.2, ease });
        gsap.to(detailsInactive, { opacity: 0, zIndex: 12, onComplete: () => { gsap.set(detailsInactive, { visibility: 'hidden' }); }});
        const width = container.offsetWidth;
        const height = container.offsetHeight;
        const offsetTop = height - 210;
        const offsetLeft = width - 450;
        gsap.to(`#card${active}`, { x: 0, y: 0, width: width, height: height, borderRadius: 0, ease, duration: 0.7, zIndex: 20 });
        gsap.to(`#card-content-${active}`, { opacity: 0, duration: 0.2 });
        gsap.to(".progress-sub-foreground", { width: `${(100 / data.length) * (active + 1)}%`, ease });
        rest.forEach((i, index) => {
            const xNew = offsetLeft + index * (cardWidth + gap);
            gsap.to(`#card${i}`, { x: xNew, y: offsetTop, width: cardWidth, height: cardHeight, borderRadius: 8, ease, zIndex: 30 });
            gsap.to(`#card-content-${i}`, { x: xNew, y: offsetTop + cardHeight - 95, opacity: 1, ease, zIndex: 40 });
            gsap.to(`#slide-item-${i}`, { x: (index + 1) * numberSize, ease });
        });
        setTimeout(() => { isAnimating = false; }, 1000);
    }

    function handleNav(dir) { clearTimeout(autoPlayTimeout); step(dir); startAutoPlay(); }
    function startAutoPlay() { autoPlayTimeout = setTimeout(() => { step(1); startAutoPlay(); }, 3400); }

    let loaded = 0;
    data.forEach(d => {
        const img = new Image();
        img.src = d.image;
        img.onload = () => { if(++loaded === data.length) init(); };
    });
})();
</script>
  
まつゆう
アラフォーゲーマーがいろんなゲームを楽しみながらプレイしています。YouTube・Twitch・Mixerで配信中!! 東京パフォーマンスドールの狂信者。 好きすぎてドメインを取得してファンサイトを運営中。

0 comments:

コメントを投稿