owned mediaウェブ制作に役立つコンテンツを発信中!

Vue.jsでページャー付きの全画面表示で水平スクロールのウェブページを作成する

最終更新日: Update!!
近年のウェブサイトは様々なデザインのものが存在しますが、その中でもコンテンツが全画面で水平スクロールになっているものをたまに見かけます。そこで今回はCSSで全画面の水平スクロールのレイアウトにしたものに、Vue.jsを使ってコンテンツ間のページャーを設置したウェブページを作成してみたいと思います。ページ全体が大きなスライドショーのようなイメージですね、アニメーションなどと組み合わせることでより目を引くサイトデザインに仕上げることができます。それでは実際にコードをみていきます。   まずはHTMLからです。今回はコンテンツが全画面ということで、ページャーは画面下部の前面へ重なるように配置させます。そのため、body直下にページ全体のコンテナ要素を作成します。その中に水平スクロールコンテンツの要素と、ページャー要素を配置します。水平スクロール要素の中には、コンテンツ群をまとめたラッパー要素を作成し、その中にコンテンツのセクション要素をコンテンツの数だけ作成します。コンテンツセクションにはアンカー先の情報となるid属性を指定しておきます。ちなみにコンテンツ群をまとめたラッパー要素を親要素で囲った理由はCSSで水平のスクロールバーの高さ分を隠すためです。 【HTML(Pug)】※一部抜粋
body
  div.container
    div(class="horizontal-scroll-content" id="app" ref="horizontal_scroll_content")
      div.wrapper
        section.inner#section_01 セクション1のコンテンツ
        section.inner#section_02 セクション2のコンテンツ 
        section.inner#section_03 セクション3のコンテンツ
        section.inner#section_04 セクション4のコンテンツ
        section.inner#section_05 セクション5のコンテンツ
        section.inner#section_06 セクション6のコンテンツ
    div.pager
      span.prev
        span(v-if="current === 2" @click="anchor(1, 'section_01')") PREV
        span(v-else-if="current === 3" @click="anchor(2, 'section_02')") PREV
        span(v-else-if="current === 4" @click="anchor(3, 'section_03')") PREV
        span(v-else-if="current === 5" @click="anchor(4, 'section_04')") PREV
        span(v-else-if="current === 6" @click="anchor(5, 'section_05')") PREV
      span.next
        span(v-if="current === 1" @click="anchor(2, 'section_02')") NEXT
        span(v-else-if="current === 2" @click="anchor(3, 'section_03')") NEXT
        span(v-else-if="current === 3" @click="anchor(4, 'section_04')") NEXT
        span(v-else-if="current === 4" @click="anchor(5, 'section_05')") NEXT
        span(v-else-if="current === 5" @click="anchor(6, 'section_06')") NEXT
  script(src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scroll/16.1.3/smooth-scroll.min.js")
  続いてページャーはデザインに合わせて作成しますが、今回は前のセクションに戻る用と、次のセクションに進む用の2つのページャーを配置させる形になります。現在表示されているセクションに合わせて、アンカー先を変更する必要があるので、v-ifディレクティブで切り替えられるようにしておきます。ページャーにはv-onディレクティブで、クリックイベントによって指定したハッシュが付与されるようにして、指定したコンテンツに移動できるようにします。最後に「smooth-scroll.js」をCDNなどで読み込んでおくことで一部ブラウザでうまく動作しないスクロール時のアニメーションが同じように挙動するようになります。   そして今回重要となるCSSです。まずはhtml要素と、水平スクロールでoverflow: auto;の設定をしている要素に対して「scroll-behavior: smooth;」を指定しておきます。こうすることでアンカーで移動した時にスクロールのアニメーションが追加されます。コンテンツは全画面で表示させるので、水平スクロールコンテンツ要素は幅と高さを画面いっぱいに広がるようにします。そして水平スクロールバーを隠すためにoverflow: hidden;とします。 【CSS(SASS)】※一部抜粋
html
  scroll-behavior: smooth
  .container
    position: relative
    .horizontal-scroll-content
      width: 100%;
      height: 100vh;
      overflow: hidden;
      .wrapper
        width: 100%;
        height: calc(100vh + 16px);
        scroll-snap-type: x mandatory;
        overflow-y: hidden;
        overflow-x: auto;
        position: relative;
        scroll-behavior: smooth;
        .inner
          width: 100%;
          height: 100%;
          scroll-snap-align: start;
          position: absolute;
          top: 0;
          left: 0;
    .pager
      position: absolute
      width: 100%;
      z-index: 1;
      left: 0;
      bottom: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      & > span
        flex-basis: 50%;
        &.next
          text-align: right;
  続いてコンテンツ群をまとめたラッパー要素ですが、水平方向にスクロールができるよう「overflow-x: auto;」の指定をします。この時に「scroll-snap-type: x mandatory;」を指定しておくとスクロールした時にコンテンツを基準にスナップ表示させることができます。また、このラッパー要素の子要素となるコンテンツセクションを水平に並べるために「position: relative;」と設定します。最後にポイントとなるのは「height: calc(100vh + 16px);」とすることで水平スクロールバーを画面外にはみ出させて隠すことができます。   コンテンツセクション要素は絶対位置指定で水平に並べるので「position: absolute;」を忘れずに指定します。親要素でスクロールスナップの指定をしている場合には「scroll-snap-align: start;」などの設定もしておきましょう。最後にページャーをページ下部の前面に固定表示されるようにCSSを設定します。これで基本的なレイアウトが完成しましたが、現時点ではコンテンツが全て同じ場所に重なって表示されるため、最後にJavaScriptを使ってコンテンツを水平の1行内に収まるよう配置させていきます。   それではJavaScriptを見ていきます。今回はVue.jsを使うため、最初にインスタンスを作成したら対象の要素をマウントさせておきます。続けて「data」オプションでは現在表示されているコンテンツの順番を示す数値を変数として持たせておきます。 【JavaScript(Vue)】
const vueModel = new Vue({
  el: '#app',
  data() {
    return {
      current: 1
    }
  },
  mounted() {
    this.init();
    this.doObserve('.inner');
  },
  methods: {
    init() {
      const targets = document.querySelectorAll('section');
      Array.from(targets).forEach((target, index) => {
        if (index[0]) {
          target.style.left = 0;
        } else {
          target.style.left = (index * 100) + '%';
        }
      });
    },
    doObserve(element) {
      const targets = document.querySelectorAll(element);
      const options = {
        root: this.$refs.horizontal_scroll_content,
        rootMargin: '0px',
        threshold: 0.25
      };
      const observer = new IntersectionObserver((items) => {
        items.forEach((item) => {
          if (item.isIntersecting) {
            item.target.classList.add('observed');
            if(item.target.id === 'section_01') {
              this.current = 1;
            } else if(item.target.id === 'section_02') {
              this.current = 2;
            } else if(item.target.id === 'section_03') {
              this.current = 3;
            } else if(item.target.id === 'section_04') {
              this.current = 4;
            } else if(item.target.id === 'section_05') {
              this.current = 5;
            } else if(item.target.id === 'section_06') {
              this.current = 6;
            }
          } else {
            item.target.classList.remove('observed');
          }
        });
      }, options);
      Array.from(targets).forEach((target) => {
        observer.observe(target);
      });
    },
    anchor(section, hash) {
      location.hash = hash;
      this.current = section;
    }
  }
});
  「methods」オプションでは今回必要となる処理を書いていきます。必要となるのは「コンテンツを水平にレイアウトする処理」と「ページャー用のアンカー先指定の処理」と「スクロール時に現在表示されているコンテンツの情報を更新する処理」の3つになります。まず、コンテンツを水平に並べる方法ですが、全てのコンテンツ要素を配列として取得し、それに対して「forEach」メソッドを使って前のコンテンツの左側に配置されるようスタイルを設定していきます。この時にインデックス番号を使うことで、全てのコンテンツを綺麗に横一列に並べることができます。この処理を最後に「mounted」で実行させるようにすると表示された時にはレイアウトが整っている状態となります。   続いて、スクロール時に現在表示されているコンテンツの情報を更新する処理ですが、その辺りについては過去記事「JavaScriptのIntersectionObserverAPIでビューポート内での表示を検知する」で紹介している方法を活用すると便利です。表示されているコンテンツがもつid属性の値を取得し、その値に合わせて現在表示されているコンテンツを示す変数を更新します。最後に、アンカー先指定ですが、引数に表示させるコンテンツ順を示す番号とアンカーとなるハッシュの値が入るようにしておき、ハッシュが示す場所に移動させたのち、現在表示されているコンテンツの情報を更新させるようにしておきます。このメソッドはHTML側でページャーのクリックイベントとして呼び出します。   このようにしてページャー付きの全画面表示の水平スクロールで動くウェブページが完成しました。実際のサンプルはこちらにアップロードしていますのでご参考ください。   ちなみにですが、下記のようにページャーを使わない場合で、aタグで直接アンカーに飛ばす場合には、a要素に対して、「scrollIntoView({ behavior: "smooth" });」というメソッドを実行させておかないと正しくスクロールのアニメーションが実行されない場合があるので注意しておきます。
// HTML
<a href="#section_01">セクション1へ移動する</a>

// JavaScript
const paginations = document.querySelectorAll('a');
paginations.forEach((pagination) => {
  pagination.addEventListener('click', e => {
    e.preventDefault();
    const targetId = e.target.hash;
    const target = document.querySelector(targetId);
    target.scrollIntoView({ behavior: "smooth" });
  });
});
 
  今回は、Vue.jsでページャー付きの全画面表示で水平スクロールのウェブページを作成してみました。今回は紹介していませんが、全コンテンツへ自由に移動できるページネーションをつけるなどの応用もできるかと思いますので、ぜひ色々と試してみてはいかがでしょうか。今回のサンプルはこちらからご確認いただけますのでご参考に。
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

    • Twitter
    • Github
    contact usscroll to top
      • Facebook
      • Twitter
      • Github
      • Instagram