<div class="scroll-anim-30" id="js-scroll-anim-30">
    <ul class="scroll-anim-30__items">
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/237/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/247/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/239/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/248/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/249/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/242/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/243/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/244/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/250/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/251/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/252/240/480" /></div>
            </a></li>
        <li class="scroll-anim-30__item"><a class="scroll-anim-30__item-link">
                <div class="scroll-anim-30__item-img"><img src="https://picsum.photos/id/253/240/480" /></div>
            </a></li>
    </ul>
</div>
.scroll-anim-30#js-scroll-anim-30
  ul.scroll-anim-30__items
    - for (var i = 0; i < items.length; i++)
      li.scroll-anim-30__item
        a.scroll-anim-30__item-link
          .scroll-anim-30__item-img
            img(src=items[i])
{
  "items": [
    "https://picsum.photos/id/237/240/480",
    "https://picsum.photos/id/247/240/480",
    "https://picsum.photos/id/239/240/480",
    "https://picsum.photos/id/248/240/480",
    "https://picsum.photos/id/249/240/480",
    "https://picsum.photos/id/242/240/480",
    "https://picsum.photos/id/243/240/480",
    "https://picsum.photos/id/244/240/480",
    "https://picsum.photos/id/250/240/480",
    "https://picsum.photos/id/251/240/480",
    "https://picsum.photos/id/252/240/480",
    "https://picsum.photos/id/253/240/480"
  ]
}
  • Content:
    $BLOCK_NAME: '.scroll-anim-30';
    
    // 変数
    $item_skew_deg: -10deg;
    $item_offset: 16px;
    
    #{ $BLOCK_NAME } {
      background: #000;
      padding: 144px 24px;
    
      &__items {
        width: 800px;
        display: grid;
        gap: 24px;
        grid-template-columns: repeat(4, 1fr);
        margin: 0 auto;
      }
    
      &__item {
        position: relative;
        height: 240px;
        &:nth-child(4n + 1) {
          transform: skewY($item_skew_deg) translateY(-$item_offset * 2);
        }
        &:nth-child(4n + 2) {
          transform: skewY($item_skew_deg) translateY(-$item_offset);
        }
        &:nth-child(4n + 3) {
          transform: skewY($item_skew_deg) translateY($item_offset);
        }
        &:nth-child(4n) {
          transform: skewY($item_skew_deg) translateY($item_offset * 2);
        }
        &::before,
        &::after {
          position: absolute;
          top: 0;
          bottom: 0;
          left: 0;
          right: 0;
          content: '';
          border: 1px solid #fff;
          opacity: 0;
        }
        &::before {
          transform: scaleY(2) translate(-8px, -8px);
        }
        &::after {
          transform: scaleY(2) translate(8px, 8px);
        }
        &.--animated {
          &::before {
            animation: scrollAnim30TopBorder 0.3s forwards;
          }
          &::after {
            animation: scrollAnim30BottomBorder 0.3s forwards;
          }
        }
      }
    
      &__item-link {
        position: relative;
        display: block;
        width: 100%;
        height: 100%;
        border: 1px solid rgba(#fff, 0.5);
        overflow: hidden;
        &::after {
          position: absolute;
          top: 0;
          bottom: 0;
          left: 0;
          right: 0;
          content: '';
          background-color: #333;
          z-index: 1;
        }
        @at-root #{ $BLOCK_NAME }__item.--animated & {
          &::after {
            animation: scrollAnim30ItemCover 0.3s 0.2s forwards;
          }
        }
      }
    
      &__item-img {
        position: absolute;
        top: -24px;
        bottom: -24px;
        left: 0;
        right: 0;
        transform: skewY(-$item_skew_deg);
        > img {
          width: 100%;
          height: 100%;
          object-fit: cover;
        }
      }
    }
    
    @keyframes scrollAnim30TopBorder {
      0% {
        opacity: 0;
        transform: scaleY(2) translate(-8px, -8px);
      }
      30% {
        opacity: 1;
        transform: scaleY(2) translate(-8px, -8px);
      }
      80% {
        opacity: 1;
      }
      100% {
        opacity: 0;
        transform: scaleY(1) translate(0, 0);
      }
    }
    
    @keyframes scrollAnim30BottomBorder {
      0% {
        opacity: 0;
        transform: scaleY(2) translate(8px, 8px);
      }
      30% {
        opacity: 1;
        transform: scaleY(2) translate(8px, 8px);
      }
      80% {
        opacity: 1;
      }
      100% {
        opacity: 0;
        transform: scaleY(1) translate(0, 0);
      }
    }
    
    @keyframes scrollAnim30ItemCover {
      0% {
        background-color: #333;
      }
      50% {
        background-color: #ccc;
      }
      100% {
        background-color: transparent;
      }
    }
    
  • URL: /components/raw/scroll-anim30/scroll-anim30.scss
  • Filesystem Path: src/components/scroll-anims/scroll-anim30/scroll-anim30.scss
  • Size: 2.7 KB
  • Content:
    'use strict';
    
    import { gsap } from 'gsap';
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    gsap.registerPlugin(ScrollTrigger);
    
    export const scrollAnim30 = () => {
      const anim = new Anim30('js-scroll-anim-30');
      anim.init();
    };
    
    class Anim30 {
      el: HTMLElement;
      itemsEl: HTMLElement;
      constructor(elId: string) {
        this.el = document.getElementById(elId);
        if (!this.el) return;
        this.itemsEl = this.el.querySelector('.scroll-anim-30__items');
      }
    
      /**
       * 初期化
       */
      init(): void {
        if (!this.el) return;
        this.scrollHandler();
      }
    
      /**
       * スクロール連動のイベント設定
       */
      scrollHandler(): void {
        ScrollTrigger.create({
          trigger: this.el,
          start: 'top 50%',
          onEnter: (self) => {
            [...this.itemsEl.children].forEach((el, index) => {
              const columnNum = 4;
              const delayUnit = 50;
              const row = Math.floor(index / columnNum);
              const column = index < columnNum - 3 ? index : index % 4;
              const delay = (row + column) * delayUnit;
              setTimeout(() => {
                el.classList.add('--animated');
              }, delay);
            });
            self.kill();
          },
        });
      }
    }
    
  • URL: /components/raw/scroll-anim30/scroll-anim30.ts
  • Filesystem Path: src/components/scroll-anims/scroll-anim30/scroll-anim30.ts
  • Size: 1.2 KB

参考