見出し画像

#70 scrollTrigger.jsを試してみる

https://www.willstyle.co.jp/blog/2869/  の記事を見て、難しかったので、写経することで内容を理解しようとした試み。

基礎 ゆっくり表示

まずはCDNの読み込み

<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollTrigger/1.0.3/ScrollTrigger.min.js"></script>

scrollTrigger.jsを呼び出す

<script>
 const trigger = new ScrollTrigger.default()
 trigger.add('[data-trigger]')
</script>

HTML側にトリガー(data-trigger)をセット

<p data-trigger>contents1</p>

scrollTrigger.jsの基本的な動きは、見えるようになったら、invisibleクラスが消えてvisibleクラスが付くというもの。

CSSで visibleとinvisibleを設定

.visible, .invisible {
  opacity: 0;
  transition: opacity .5s ease .5s;
}
.visible {
  opacity: 1;
}

デモ

中級 HTMLにクラスを付与して、動きをカスタマイズ

下から上にフェードインする動きにします。
HTMLに  class="translateY" を追加

<div class="wrapper"><p data-trigger class="translateY">contents1</p></div>
<div class="wrapper"><p data-trigger class="translateY">contents2</p></div>
<div class="wrapper"><p data-trigger class="translateY">contents3</p></div>

CSSでtranslateYに、100px下の位置から、0の位置まで移動するように設定

.visible.translateY,.invisible.translateY {
  transform: translateY(100px);
  transition: all 0.5s cubic-bezier(0.165, 0.840, 0.440, 1.000) .5s;
}
.visible.translateY {
  transform: translateY(0);
}

デモ

上級1 表示開始位置をカスタマイズする

見えるタイミングが、ブラウザのどの位置に来てから見えるようにするか、カスタマイズする方法 elementとviewportの数値を調整。0.1ならブラウザ(viewport)の上もしくは下から10%の位置にelementが来たら見えるようになる。

once: trueは1回だけ実行するか、once: falseは何度も実行するか。

const trigger = new ScrollTrigger.default()
trigger.add('[data-trigger]', {
    once: false,
    offset: {
        element: {
            x: 0,
            y: (trigger, rect, direction) => {
                return 0.2 //windowの高さの上下から20%の位置にelementが来たときにトリガーされる
            }
        },
        viewport: {
            x: 0,
            y: (trigger, frame, direction) => {
                return trigger.visible ? 0 : 0.2 //windowの高さの上下から20%の位置
            }
        }
    }
})

デモ

上級2 JS側でクラスの付け替えをする

toggleのclassメソッドでinの時にactionクラスを付ける

trigger.add('[data-trigger]', {
    once: false,
    offset: {
        element: {
            x: 0,
            y: (trigger, rect, direction) => {
                return 0.2 //windowの高さの上下から20%の位置にelementが来たときにトリガーされる
            }
        },
        viewport: {
            x: 0,
            y: (trigger, frame, direction) => {
                return trigger.visible ? 0 : 0.2 //windowの高さの上下から20%の位置
            }
        }
    },
    toggle: {
        class: {
            in: ['visible', 'action'], //見えた時に、actionというクラスを付与できる
            out: ['invisible']  //表示から外れた時に、invisibleというクラスを付与
        }
    },
})

上級3 JS側で見えた時に関数を実行して動きをよりカスタマイズする

見えると、一文字ずつ大きさが変わる表現

anime.jsもCDNから読み込む

<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.1.0/anime.min.js"></script>

HTMLでdata-triggerに値をセット。 data-trigger="scale"とする。
一文字ずつ分割したいので、分割用に親クラスclass="str__animation"を設定

<div class="wrapper" data-trigger="scale"><p class="str__animation">contents1</p></div>
<div class="wrapper" data-trigger="scale"><p class="str__animation">contents2</p></div>
<div class="wrapper" data-trigger="scale"><p class="str__animation">contents3</p></div>

class="str__animation"の中をspanで分割する

const str_animations = document.querySelectorAll('.str__animation');

if(str_animations.length > 0){
    str_animations.forEach(element => {
        let set_str = "";
        const strs = element.innerText.split("");

        if(strs.length > 0){
                strs.forEach(str => {
                    set_str += '<span class="str">' + str + '</span>';
            });
        }
        element.innerHTML = set_str;
    });
}

CSS側で、str__animationをflexにして、中のspanをblock化する。
中の<span class="str">の最小の大きさをmin-width: .3rem;にする

.str__animation{
    display: flex;
}
.str{
  min-width: .3rem;
}

scrollTriggerのcallbackでanime.jsを呼び出し、各種項目を設定
scaleを3から1にすることで大きさを変更

const trigger = new ScrollTrigger.default()

trigger.add('[data-trigger="scale"]', {
    once: false,
    offset: {
        element: {
            x: 0,
            y: (trigger, rect, direction) => {
                return 0.2 //windowの高さの上下から20%の位置にelementが来たときにトリガーされる
            }
        },
        viewport: {
            x: 0,
            y: (trigger, frame, direction) => {
                return trigger.visible ? 0 : 0.2 //windowの高さの上下から20%の位置
            }
        }
    },
    toggle: {
        class: {
            in: ['visible', 'action'], //見えた時に、actionというクラスを付与できる
            out: ['invisible']  //表示から外れた時に、invisibleというクラスを付与
        },
        callback: {
            in: (trigger) => {               
                const strs = trigger.element.querySelectorAll('span');

                anime({
                    targets: strs,
                    scale:[3,1],
                    easing: 'easeInOutExpo',
                    duration: 800,
                    opacity:[0,1],
                    delay: function(el, i) { return i * 20 }
                });

            }
        }
    },
})

デモ

上級4 movingLetter.jsと合わせてみる

↑ のコードと合わせてみます。

<h1 class="wrapper ml7" data-trigger="letter">
  <span class="text-wrapper">
    <span class="letters">Reality is broken1</span>
  </span>
</h1>
<h1 class="wrapper ml7" data-trigger="letter">
  <span class="text-wrapper">
    <span class="letters">Reality is broken2</span>
  </span>
</h1>
<h1 class="wrapper ml7" data-trigger="letter">
  <span class="text-wrapper">
    <span class="letters">Reality is broken3</span>
  </span>
</h1>

CSS

.ml7 {
  position: relative;
  font-weight: 900;
  font-size: 3.7em;
}
.ml7 .text-wrapper {
  position: relative;
  display: inline-block;
  padding-top: 0.2em;
  padding-right: 0.05em;
  padding-bottom: 0.1em;
  overflow: hidden;
}
.ml7 .letter {
  transform-origin: 0 100%;
  display: inline-block;
  line-height: 1em;
}

JS

const letters = document.querySelectorAll('.letters');

if(letters.length > 0){
    letters.forEach(element => {
        let set_str = "";
        const strs = element.innerText.split("");

        if(strs.length > 0){
                strs.forEach(str => {
                    set_str += '<span class="letter">' + str + '</span>';
            });
        }
        element.innerHTML = set_str;
    });
}
    
const trigger = new ScrollTrigger.default()
trigger.add('[data-trigger="letter"]', {
    once: true,
    offset: {
        element: {
            x: 0,
            y: (trigger, rect, direction) => {
                return 0.2 //windowの高さの上下から20%の位置にelementが来たときにトリガーされる
            }
        },
        viewport: {
            x: 0,
            y: (trigger, frame, direction) => {
                return trigger.visible ? 0 : 0.2 //windowの高さの上下から20%の位置
            }
        }
    },
    toggle: {
        callback: {
            in: (trigger) => {             

            anime.timeline({loop: false})
              .add({
                targets: '.ml7 .letter',
                opacity: [0, 1],
                translateY: ["1.1em", 0],
                translateX: ["0.55em", 0],
                translateZ: 0,
                rotateZ: [180, 0],
                duration: 750,
                easing: "easeOutExpo",
                delay: (el, i) => 50 * i
              });

            }
        }
    },
})

デモ

参考


https://github.com/terwanerik/ScrollTrigger

https://terwanerik.github.io/ScrollTrigger/

ヘッダーは伊藤若沖の葡萄双鶏図

この記事が気に入ったらサポートをしてみませんか?