コーポレートサイトをリニューアルしました

こんにちは、フロントエンドエンジニアの権守です。 既にお気づきの方も多いと思われますが、こちらのテックブログは今月から装いを新たにしています。これは先日行った弊社コーポレートサイトのリニューアルに合わせたものです。 この記事では、今回行ったコーポレートサイトリニューアルについて実装面から紹介します。 f:id:vasilyjp:20170727174903p:plain

特徴

今回のリニューアルの特徴は以下の3つです。

  • Vue.jsによるSPA (Single Page Application)としての実装
  • レスポンシブ対応
  • アニメーションによるリッチな表現

それぞれについて詳しく紹介していきます。

Vue.js

Vue.jsはユーザーインタフェースを構築するためのJSフレームワークの1つです。Reactなどのフレームワークと同様にSPAの実装に使えますが、段階的に導入していくことも可能な点が特徴です。 その段階的に導入可能という点が、既存のサービスへの導入を容易にしていることもあり、弊社ではVue.jsを一部導入しています。現状、段階的に使っている上ではVue.jsは非常に便利という印象でしたが、今後、本格的に導入していく上でSPAフレームワークとしても満足のいくものであるかはわかりませんでした。 そこで、今回はVue.jsのSPAフレームワークとしての使い勝手を検証する意味も込めてコーポレートサイトの構築に全面的に導入しました。

Vue.jsの優れた点

結論から言うと、Vue.jsはSPAフレームワークとしても非常に便利であり、今後SPAを開発する際に弊社で採用する可能性は非常に高いと思います。 その判断に至った大きな要因は単一ファイルコンポーネントによる関心の分離のスマートさです。Vue.jsの単一ファイルコンポーネントでは、こちらで述べられている「関心の分離はファイルタイプの分離と等しくない」という理論の元、作られています。具体的には1つのファイルにテンプレート(HTML)・ロジック(JavaScript)・スタイル(CSS)の記述を可能としており、一貫性と保守性を高めています。 以下にコーポレートサイト内で用いている単一ファイルコンポーネントの例を示します。

main_heading.vue
<template lang="pug">
  h1 {{title}}
</template>
<script>
export default {
  name: 'heading',
  props: ['title'],
}
</script>
<style lang="sass" scoped>
@media (max-width: 1039px)
  h1
    display: none
@media (min-width: 1040px)
  h1
    color: #333
    text-align: center
    font-family: 'DIN'
    font-weight: bold
    font-size: 30px
    letter-spacing: 0.75px
    line-height: 200px
</style>

上の例からテンプレート・ロジック・スタイルが1つのファイルに書かれていることがよくわかると思います。また、style部分にscopedと書かれていますが、これは単一ファイルコンポーネントが有するScoped CSSという機能を利用することを示しています。この機能を利用することで、そのコンポーネント内に記述されたスタイルが他のコンポーネントを汚染することを心配する必要がありません。それによってネームスペースを確保するためだけの余計なクラスを付与する必要もなくなります。これらの徹底した関心の分離はアプリケーションが大規模になるほど、より効果的になると考えています。

レスポンシブ対応

f:id:vasilyjp:20170727180655p:plain PC、タブレット、スマホといった具合にサイトを訪れる方のデバイスは様々です。それらのどれでアクセスしたとしても、弊社の魅力を最大限伝えられるように、レスポンシブ対応を全面的に行いました。 CSSのメディアクエリを用い、画面サイズによってPC用とモバイル用でスタイルを切り替えています。また、一部コンポーネントではモバイル版の中でもサイズによってレイアウトを2カラムから3カラムに切り替えるといったことも行っています。 CSSだけでなく、以前書いた記事で紹介した画像のレスポンシブ対応ももちろん取り入れています。

実際にどのようにCSSによるレスポンシブ対応をしているかの雰囲気がわかるように2カラムから3カラムに切り替わるコンポーネントのスタイル部分を示します。

article_card.vue
<style lang="sass" scoped>
article
  display: block
  overflow: hidden
  text-align: left
  .container
    position: relative
    width: 100%
    overflow: hidden
  img
    position: absolute
    top: 50%
    left: 50%
    -webkit-transform: translate(-50%, -50%)
    transform: translate(-50%, -50%)
    height: 100%
    margin: auto
  .blog
    display: inline-block
    color: #333
    font-family: 'DIN'
    font-weight: bold
    &:hover
      text-decoration: underline
  time
    display: inline-block
    float: right
    color: #999
    font-size: 10px
  .title
    display: block
    display: -webkit-box
    -webkit-box-orient: vertical
    -webkit-line-clamp: 3
    overflow: hidden
    color: #333
    text-align: left
@media (max-width: 330px)
  article
    .title
      font-size: 10px
      line-height: 15px
@media (min-width: 331px) and (max-width: 1039px)
  .title
    font-size: 11px
    line-height: 17px
@media (max-width: 557px)
  article
    &:before
      padding-top: 153.62%
    width: calc(50% - 5px)
@media (min-width: 558px) and (max-width: 1039px)
  article
    &:before
      padding-top: calc(100% + 90px)
    width: calc(33.3% - 6.66px)
@media (max-width: 1039px)
  .meta
    padding: 0 3px 0 5px
  .blog
    font-size: 12px
    margin-top: 13px
  .title
    margin-top: 7px
  time
    margin: 14px 2px 0 0
  article
    >a
      width: 100%
      height: 100%
      display: block
      position: absolute
      top: 0
      left: 0
    &:before
      display: block
      content: ''
  .container
    &:before
      display: block
      content: ''
      padding-top: 100%
@media (min-width: 1040px)
  article
    width: 260px
    height: 310px
    transition: margin-top 0.25s ease-out
    &:hover
      margin-top: -20px
    .container
      height: 200px
  .blog
    font-size: 13px
    margin-top: 15px
  time
    margin-top: 15px
  .title
    font-size: 13px
    line-height: 20px
    margin-top: 9px
</style>

また、このレイアウトが切り替わった際の要素の再配置部分はMasonryを用いて実装しました。CSSだけで実装しなかったのは、高さの異なる要素を縦方向に詰めて配置することができなかったからです。

アニメーションによるリッチな表現

今回のサイトデザインは、読みやすさを重視したシンプルなデザインな一方で、冷たい印象になりすぎないようにリッチなアニメーションを散りばめています。アニメーションの実装にはCSS3のアニメーションを中心に使いつつ、より複雑なアニメーションが求められる箇所ではD3.jsを用いました。 2つピックアップして実装について紹介します。

lazy-box

スクロールに応じて、コンテンツが出現するアニメーションをlazy-boxというコンポーネントとして実装しました。

lazy_box.vue
<template lang="pug">
  .lazy-box(ref="lazyBox" :class="{active: active}")
    slot(:active="active")
</template>
<script>
export default {
  mounted() {
    window.addEventListener('scroll', this.handleScroll);
    this.handleScroll();
  },
  destroyed() {
    window.removeEventListener('scroll', this.handleScroll);
  },
  data() {
    return {
      active: false,
      handleScroll: (e) => {
        if (window.innerHeight - this.$refs.lazyBox.getBoundingClientRect().top > 0) {
          this.active = true;
        }
      }
    }
  }
}
</script>
<style lang="sass" scoped>
  .lazy-box
    opacity: 0
    transition: opacity 2s, transform 0.5s
    @media (max-width: 1039px)
      transform: translateY(100px)
    @media (min-width: 1040px)
      transform: translateY(200px)
    &.active
      opacity: 1
      transform: translateY(0)
</style>

lazy-boxはVue.jsのコンポーネントのslot機能を用いることで任意のコンテンツに対してアニメーションを付与することが可能です。

D3.jsを用いたグラフ

RecruitページのグラフはD3.jsを使って実装しています。D3.jsはデータに基づいてドキュメントを操作するライブラリで、データ可視化によく用いられます。今回は、棒グラフとその数値のアニメーションの実装に用いました。 具体的には次に示すようなメソッドをVueコンポーネントのmethodsに設定し、コンポーネントがactiveになったタイミングで各メソッドを呼ぶようにしています。

methods: {
  barTransition() {
    var selection = d3.select(this.$refs.bar);
    selection
      .transition()
      .ease(d3[this.ease])
      .duration(this.duration)
      .styleTween("width", () => {
        var i = d3.interpolate(0, '100%');
        return function(t) {
          return i(t);
        };
      });
  },
  valueTransition() {
    var selection = d3.select(this.$refs.value);
    var value = this.value;
    selection
      .transition()
      .ease(d3[this.ease])
      .duration(this.duration)
      .tween("span", function(d) {
        var i = d3.interpolate(0, value);
        var self = this;
        return function(t) {
          d3.select(self).text(parseInt(i(t)));
        };
      });
  }
}

まとめ

今回は、先日行った弊社のコーポレートサイトリニューアルについてその特徴と実装について紹介しました。 レスポンシブ対応をしっかり行ったので、画面サイズの変更やランドスケープモードへの切り替えなど、ぜひ色々試しながらVASILYのことを今一度知っていただけると幸いです。

最後に

VASILYでは、積極的に挑戦していけるエンジニアを募集しています。興味のある方は以下のリンクからぜひご応募ください。 www.wantedly.com