BETA

特殊な形のパララックス(svgでの実装)

投稿日:2018-10-17
最終更新:2018-10-24
※この記事は外部サイト(https://qiita.com/mild_tanama/items/eaba67...)からのクロス投稿です

はじめに

パララックスは視差のことを指しており、例えば背景画像と手前の要素でスクロールする時の移動速度に差を設けることで、要素同士に距離があるように感じさせる効果などがあります。 今回はスクロール量に応じて背景画像が少し動くことで、奥行きを感じさせるような動作についての実装をさせています。特に矩形以外の特殊な形に切り取られた要素を想定しています。

なんで作ろうかと思ったのかは以下のサイト見たからです クロッカンシュー ザクザク [CROQUANTCHOU ZAKUZAKU] svgの形式などは大いに参考にさせて頂きました。

その実装の中で学んだことなどを記しておりますので、これからパララックス作る方へ参考までに。

デモ

See the Pen Parallax svg clip-path by mild-tanama (@mild-tanama) on CodePen.

実装方法

cssかsvg

実装方法は様々あると思いますが、大きくわけるのであればcssを使うか、svgを使うかだと思います。どちらもclip-pathかmaskが使えますね。 矩形じゃないのであれば、これらの選択になると思います。 (他にございましたらコメントお願いします) (ゴリ押しなら画像をパラパラ漫画みたいに動かす方法もありますね…)

今回svgを選びましたのは、単純に対応の問題です。 https://caniuse.com/#search=clip-path 実はcssのclip-path, mask両方ともIE11, Edge17が未対応です。 未だに多いIE11のシェア率を見過ごすわけにはいかず、svgでの実装を選びました。 ちなみにsvgはIE8未満は対応していませんので、場合によりけりです。

流れ

  • htmlにsvgを置く
  • jsでスクロール量に合わせて画像を動かす
  • jsでリサイズ処理をかく

細かいところ省いてこのような流れになってます。 以下で1つずつピックして書きます。

htmlにsvgを置く

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="600" baseProfile="full" version="1.2">
    <defs>
        <clipPath id="imageMask">
            <path d="M0,0 l1000,180 v240 L0,500 z"></path>
        </clipPath>
    </defs>
    <g clip-path="url(#imageMask)">
        <a xlink:href="https://www.youtube.com/watch?v=Ymnfiq0BtAY" target="_blank">
            <image id="img" width="100%" height="150%" y="-50%" xlink:href="https://dl.dropbox.com/s/4amw0vilxbty9tx/dove-2680487_1920.jpg?dl=0" preserveAspectRatio="xMidYMid slice"></image>
        </a>
    </g>
</svg>

こちらがsvgのコードになります。svg自体についての詳しい説明は省きます。

<defs>の中でclip pathを設定します。こちらで切り抜きを指定し、idをつけることで下のaタグや画像に切り抜き効果を適用することができます。 pathの値について詳しくは以下がわかりやすいのではないでしょうか。 https://qiita.com/a-ide/items/107c9044d0f4e0354112 デザインツールにも、svgで書き出しなどする際にpathが出てくると思いますのでそちらを利用してpathを自由に設定できます。

<g clip-path="url(#imageMask)">ここで先ほど設定したclip pathを使います。gタグで括ることで、中の要素が設定したpath通りに切り取られます。この場合だと、aタグもimageも切り取ってもらえるので、aタグのホバー範囲も切り取ってくれますよ!

imageにはpreserveAspectRatio="xMidYMid slice"で画像の表示を変更しています。 sliceで画像をアスペクト比を保ったまま画像を引き伸ばし、 xMidYMidで表示位置をど真ん中にし設定します。 参考:http://defghi1977-onblog.blogspot.com/2012/12/svgpreserveaspectratio.html あとは画像によって真ん中にしたい位置が変わってくると思うので、y="-50%"などで画像位置を調整してます。

ちなみにaタグですがsvgの場合はcursor: pointer;が設定してないらしいのでcssで設定しております。 書き方など、諸々svg内でのタグは仕様が違うので注意が必要ですね。

jsでスクロール量に合わせて画像を動かす

var SAMPLE = {
    init: function() {
        this.setParameters();
        this.setScrollPosition();
        this.setWidth();
        this.bindEvent();
    },
    setParameters: function() {
        this.$window = $(window);
        this.$container = $('.wrap');
        this.$svg = $('#imageMask')
        this.$img = this.$container.find('image');
        this.translatePosition = 0;
    },
    bindEvent: function() {
        var myself = this;
        this.$window.on('resize', function(){
            if(myself.timer !== false) {
            clearTimeout(myself.timer);
            }
            myself.timer = setTimeout($.proxy(myself.setWidth, myself), 200);
        });

        this.$window.on('scroll', $.proxy(this.setScrollPosition, this));
    },
    setWidth: function() {
        var width = this.$window.width();
        if(width === this.windowWidth) return;
        this.windowWidth = this.$window.width();
        if(width <= 1000) width = 1000;
        this.$svg.find('path').attr('d','M0,0 l'+ width +',100 v240 L0,600 z');
    },
    setScrollPosition: function() {
    var scrollLength = this.$window.scrollTop(),
        sectionNum = Math.ceil(scrollLength/this.containerHeight),
        translatePosition = scrollLength;
    if(translatePosition >= this.containerHeight){
        translatePosition -= this.containerHeight * (sectionNum-1);
    }
    this.$img.css({'transform': 'translate(0, ' + translatePosition/4 + 'px)'});
    }
}

$(window).on('load',function(){
    SAMPLE.init();
});

やっていることは

  • scrollイベントを取得(bindEvent)
  • そこで画像に対してcssのtransform: translate()を付与して動かす(setScrollPosition)

こんな感じ。

translatePosition/4 ここの4の値をいじることで、スクロールに伴う画像の移動スピードが変わります。

ちなみにposition:topなどを使わずにtransformプロパティを使っているのはパフォーマンスが良くなるからです。詳しくは以下のサイトがわかりやすいです。 https://www.webprofessional.jp/achieve-60-fps-mobile-animations-with-css3/

jsでリサイズ処理をかく

リサイズするたびにsvgのwidth設定し直してます。 svgのpathはwidth100%できないっぽかったので(こちらちゃんとわかる方いたら聞きたいです) リサイズするたびにpath内のwidthを変更してます。

this.$svg.find('path').attr('d','M0,0 l'+ width +',100 v240 L0,600 z');

まとめ

  • cssのclip-pathはIE, Edgeでは使えないが、svgは使える(しかしIE8以下は未対応)
  • svg内のaタグは普通のaタグと少し扱いが違うので注意
  • imageタグにpreserveAspectRatioを設定すると表示を変更できる

ざっと学んだことはここら辺でしょうか。 詰まったところは大体SVGの仕様だったりしたので、調べるって大事…。 clip-path、まだまだ楽しいことができそうなので、今後も勉強していきたいです。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@mildtanama'の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!