【Vue3+tailwindcss】垂直スクロールで水平アニメーション

Vue.js

Vue v3とtailwindcssを使って、垂直スクロールで画像が水平に動くアニメーションを作る方法を紹介します。

tailwindcssの導入

tailwindcssの導入方法は、Vue 3でtailwindcssを使う方法を参考にしてみてください。

template

<template>
  <div class="relative flex flex-col w-full transition duration-500 ease-in-out" :class="bgColor">
    <div ref="section1" class="relative h-screen">
      <div class="flex flex-col justify-center w-full h-full text-center">
        ここにsection1の中身
      </div>
    </div>

    <div ref="section2" class="relative block m-auto w-full" style="height:280vw">
      <div
        class="sticky top-0 flex justify-between items-center py-10 h-screen"
        style="width:280vw;will-change:transform;transform-style:preserve-3d"
        :style="{'transform' : 'translate3d('+ X + 'px, 0px, 0px)'}"
        >
        <div id="img1" class="bg-contain bg-center bg-no-repeat w-3/12 mx-6 h-full"></div>
        ここに必要なだけ画像を入れる
      </div>

    </div>
    <div class="relative h-screen">
      <div class="flex flex-col justify-center w-full text-center">
        ここにsection2の後の中身
      </div>
    </div>
  </div>
</template>

flexで全体を囲ってから、3つのパートに分けています。
真ん中がメインの部分でrefをsection2としています。

positionプロパティstickyでくっつける

positionプロパティでstickyを使うことで、そのコンテンツが終わるまではくっついた状態で常に表示されるようにになります。

heightとwidthを300vw程度に

また、divタグを3重にしていますが、その一番外側でstyleクラスでheightを約300vw程度に設定します。
同じ値をその中のdivタグにwidthで設定します。
そうすることでスクロールの領域を指定することができます。

will-changeプロパティで事前に知らせる

will-changeプロパティは、要素にどういった変更を加えるかを予めブラウザに知らせることができます。
上の場合、transformを変えますとと事前に知らせることで、レンダリング処理が速くなり、なめらかな画像処理が可能になります。
あまり頻繁に利用することが推奨されているものではないため、MDNを参照してから使いましょう。

translate3dで立体的に

そして、スクロールで水平に動かすためにはtransformプロパティのtranslate3dを使います。
translate3dはX軸、Y軸、Z軸に値を取るので、今回は水平のためX軸をスクロールで動的に変化するように設定します。
>>MDNのtranslate3d

transform-styleプロパティをpreserve-3dとすることで、子要素を3D空間的に配置することができます。

styleタグ

#img1 {
  background-image: url('/images/img1.jpg');
}
...
...画像あるだけ上にように設定
...
body {
  overflow-x: hidden;
}

css部分では、まずは背景として画像を指定します。
そして、bodyタグに見えていない部分、つまりoverflowしている部分を隠したいのでhiddenに設定します。

scriptタグ

<script>
import { defineComponent, onMounted } from 'vue'
import { ref } from '@vue/reactivity'

export default defineComponent({
  setup() {
    const X = ref(null);
    const section1 = ref(null);
    const section2 = ref(null);
    const bgColor = ref('');

    function handleXValue() {

      if (window.scrollY >= section1.value.offsetHeight) {
        X.value = -(window.scrollY - section1.value.offsetHeight);
      }

      if (X.value <= - (section2.value.offsetHeight - window.innerWidth)) {
        X.value = -(section2.value.offsetHeight - window.innerWidth)
      }

      if (window.scrollY < section1.value.offsetHeight) {
        X.value = 0;
      }
    }
    function handleBgColor() {
      // section1のとき
      if(window.scrollY <= section1.value.offsetHeight) {
        bgColor.value = 'bg-white';
      }
      // section2のとき
      if(window.scrollY >= section1.value.offsetHeight) {
        bgColor.value = 'bg-gray-800';
      }
      // section2が終わったとき
      if(window.scrollY >= section2.value.offsetHeight) {
        bgColor.value = 'bg-white';
      }
    }

    onMounted(() => {
      document.addEventListener('scroll', handleXValue)
      document.addEventListener('scroll', handleBgColor)
      });

    return {
      X,
      section1,
      section2,
      bgColor,
      handleXValue,
      handleBgColor,
    }
  },
})</script>

変数の設定

まず、変数をそれぞれ設定します。
上の場合、section1と2,そして、スクロール時に変化してほしいX軸の値をXとしてそれぞれリアクティブになるようにrefを使っています。
bgColorもsection1と2とその後で変化させるため、refを使っています。

X軸の値を操作するメソッド

handleXValue()メソッドでは、X軸の値を操作しています。
それぞれrefの値にはvalueをつけることでアクセスできます。

一つ目のif文では、window.scrollYは、現在のビューポートの上端のY座標を返します。つまりトップからの座標でscrollするたびに変化します。
section1.value.offsetHeightはsection1の高さを意味しています。
section1の高さを超えたとき、Xの値をその差にしていくので、マイナスの値になります。そのため、先頭にマイナスをつけてプラスの値にして、それをXの値として格納しています。

次のif文では、window.innerを使っています。window.innerはコンテンツ表示領域の幅を返します。
両方とも一定の数値なので、Xの値がその差を下回るとき、
Xの値を一定にする、つまり、コンテンツ表示領域の幅を維持した状態で最後の画像がずっと表示されるようになります。

最後のif文は、設定しないと

Uncaught TypeError: Cannot read property 'offsetHeight' of null

と怒られます。

背景色を操作するメソッド

背景の設定も同じように、window.scrollYとsection1の高さなどを利用して、それぞれで色が変化するように設定しています。

mountで発火

mountされたときにイベントリスナーでscrollが発火するように設定しています。
そして、作成した変数をすべて返します。

Vue.js

Posted by devsakaso