<div class="scroll-anim-24" id="js-scroll-anim-24">
    <div class="scroll-anim-24__item js-scroll-anim-24">
        <div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                    <mask class="scroll-anim-24__img-mask" id="mask0">
                        <path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
                    </mask>
                </defs>
                <foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask0)">
                    <div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1000/400/400);"></div>
                </foreignObject>
            </svg></div>
        <div class="scroll-anim-24__content">
            <div class="scroll-anim-24__heading">見出し01</div>
            <div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
        </div>
    </div>
    <div class="scroll-anim-24__item js-scroll-anim-24">
        <div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                    <mask class="scroll-anim-24__img-mask" id="mask1">
                        <path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
                    </mask>
                </defs>
                <foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask1)">
                    <div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1001/400/400);"></div>
                </foreignObject>
            </svg></div>
        <div class="scroll-anim-24__content">
            <div class="scroll-anim-24__heading">見出し02</div>
            <div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
        </div>
    </div>
    <div class="scroll-anim-24__item js-scroll-anim-24">
        <div class="scroll-anim-24__img"><svg viewBox="0 0 300 300" preserveAspectRatio="xMidYMid slice">
                <defs>
                    <mask class="scroll-anim-24__img-mask" id="mask2">
                        <path class="scroll-anim-24__img-mask-base" d="M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z"></path>
                    </mask>
                </defs>
                <foreignObject class="scroll-anim-24__img-img-wrapper" width="300" height="300" mask="url(#mask2)">
                    <div class="scroll-anim-24__img-img" style="background-image: url(https://picsum.photos/id/1002/400/400);"></div>
                </foreignObject>
            </svg></div>
        <div class="scroll-anim-24__content">
            <div class="scroll-anim-24__heading">見出し03</div>
            <div class="scroll-anim-24__text">テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。</div>
        </div>
    </div>
</div>
.scroll-anim-24#js-scroll-anim-24
  each item, index in items
    .scroll-anim-24__item.js-scroll-anim-24
      .scroll-anim-24__img
        svg(viewBox='0 0 300 300', preserveAspectRatio='xMidYMid slice')
          defs
            mask.scroll-anim-24__img-mask(id='mask' + index)
              path.scroll-anim-24__img-mask-base(d='M 0 150 l 150 -150 l 150 150 l -150 150 l -150 -150 Z')
          foreignObject.scroll-anim-24__img-img-wrapper(width='300', height='300', mask='url(#mask' + index + ')')
            .scroll-anim-24__img-img(style='background-image: url(' + item.url + ');')
      .scroll-anim-24__content
        .scroll-anim-24__heading #{ item.heading }
        .scroll-anim-24__text #{ item.text }
{
  "items": [
    {
      "url": "https://picsum.photos/id/1000/400/400",
      "heading": "見出し01",
      "text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
    },
    {
      "url": "https://picsum.photos/id/1001/400/400",
      "heading": "見出し02",
      "text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
    },
    {
      "url": "https://picsum.photos/id/1002/400/400",
      "heading": "見出し03",
      "text": "テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。テキストが入ります。"
    }
  ]
}
  • Content:
    $BLOCK_NAME: '.scroll-anim-24';
    
    // 変数
    $duration_default: 0.5s;
    $duration_delay: 0.5s;
    
    #{ $BLOCK_NAME } {
      &__item {
        display: flex;
        align-items: center;
        & + & {
          margin-top: 24px;
        }
      }
    
      &__img {
        flex-shrink: 0;
        width: 25%;
      }
    
      &__img-img {
        width: 100%;
        height: 100%;
        background-position: center center;
        background-size: cover;
      }
    
      &__content {
        width: 75%;
        padding: 24px;
        padding-left: 48px;
      }
    
      &__heading {
        font-size: 24px;
        font-weight: bold;
        opacity: 0;
        transition: all $duration_default $duration_delay;
        transform: translateX(-20px);
        @at-root #{ $BLOCK_NAME }__item.is-animated & {
          opacity: 1;
          transform: translateX(0);
        }
      }
    
      &__text {
        margin-top: 8px;
        font-size: 16px;
        opacity: 0;
        transition: all $duration_default $duration_delay;
        transform: translateX(-20px);
        @at-root #{ $BLOCK_NAME }__item.is-animated & {
          opacity: 1;
          transform: translateX(0);
        }
      }
    }
    
  • URL: /components/raw/scroll-anim24/scroll-anim24.scss
  • Filesystem Path: src/components/scroll-anims/scroll-anim24/scroll-anim24.scss
  • Size: 1.1 KB
  • Content:
    'use strict';
    
    import 'snapsvg-cjs';
    declare const Snap: any;
    import { gsap } from 'gsap';
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    gsap.registerPlugin(ScrollTrigger);
    
    export const scrollAnim24 = () => {
      const itemEls = document.querySelectorAll('.js-scroll-anim-24');
      [...itemEls].forEach(el => {
        const anim = new Anim24(<HTMLElement>el);
        anim.init();
      });
    }
    
    class Anim24 {
      el: HTMLElement;
      svg: any;
      mask: any;
      maskBase: any;
      img: any;
      maskGroups: Array<any>;
      maskPoints: Array<Array<string>>;
      constructor(el: HTMLElement) {
        this.el = el;
        if (!this.el) return;
        this.svg = Snap(this.el.querySelector('.scroll-anim-24__img svg'));
        this.mask = this.svg.select('.scroll-anim-24__img-mask');
        this.maskBase = this.svg.select('.scroll-anim-24__img-mask-base');
        this.maskGroups = [];
        this.maskPoints = [
          ['0 150'],
          ['30 120', '30 180'],
          ['60 90', '60 150', '60 210'],
          ['90 60', '90 120', '90 180', '90 240'],
          ['120 30', '120 90', '120 150', '120 210', '120 270'],
          ['150 60', '150 120', '150 180', '150 240'],
          ['180 90', '180 150', '180 210'],
          ['210 120', '210 180'],
          ['240 150'],
        ];
      }
    
      /**
       * 初期化
       */
      init(): void {
        if (!this.el) return;
        this.setMasks();
        this.scrollHandler();
      }
    
      /**
       * マスクを作成
       */
      setMasks(): void {
        this.maskPoints.forEach(points => {
          const g = this.mask.g();
          points.forEach(point => {
            const pathVal = `M ${point} l 30 -30 l 30 30 l -30 30 l -30 -30 Z`;
            const path = g.path(pathVal);
            g.add(path);
          });
          g.attr({
            fill: '#fff',
            fillOpacity: 0,
          });
          this.mask.append(g);
          // グループを配列に追加(画像表示アニメーションに利用)
          this.maskGroups.push(g);
        });
      }
    
      /**
       * 画像の表示
       */
      showImg(): void {
        this.maskGroups.forEach((group, index) => {
          // 100ms間隔でグループを表示
          setTimeout(() => {
            group.animate({
              fillOpacity: 1,
            }, 500, () => {
              // 最後にベースのマスクを表示(個別のマスクのみだと境目に線が残ってしまうため)
              if (index === (this.maskGroups.length - 1)) {
                this.maskBase.animate({
                  fill: '#fff'
                }, 500);
              }
            });
          }, 100 * index);
        });
      }
    
      /**
       * スクロール連動のイベント設定
       */
      scrollHandler(): void {
        const self = this;
        ScrollTrigger.create({
          trigger: this.el,
          start: 'top 70%',
          onEnter: self => {
            this.showImg();
            this.el.classList.add('is-animated');
            self.kill();
          }
        });
      }
    }
    
  • URL: /components/raw/scroll-anim24/scroll-anim24.ts
  • Filesystem Path: src/components/scroll-anims/scroll-anim24/scroll-anim24.ts
  • Size: 2.9 KB

No notes defined.