【サンプルあり】スクロールに連動した要素やマスクのサイズ変更【JavaScript / jQuery】
こんにちは、NNCの中里です。
Webページの表現をより豊かなものにするため、スクロールに合わせて動きをつけたい(位置の移動だけでなく要素やマスクのサイズを変更したい)時ってありますよね。
下のようなイメージです↓
スクロールに合わせて大きさが変わることでよりダイナミックな表現となり、ユーザーに与える印象も効果的なものになり、コンテンツを魅力的に見せることもできます
本日はスクロールに合わせた要素やマスクのサイズ変更について解説を行います!
目次
スクロールに合わせた要素のサイズ変更
まずはシンプルに要素のサイズ変更から行ってみましょう。
下記のコードで動作します。プレビュー画面上をスクロールしてみてください。
See the Pen Untitled by NNC STUDIO (@NNC-STUDIO) on CodePen.
HTML・CSSは至ってシンプルです。
円を描くための要素やスタイルの記述となっています。
スクロールの際に見切れてしまわないようposition: fixedで固定しています。
JavaScriptの編集ポイント
JavaScriptの考え方としては以下の通りとなります。
1. スクロール量を取得
scroll = $(this).scrollTop(); //8行目
2. 円のスケール初期値(1)にスクロール量を50で割った値を足す
sum = 1 + scroll / 50; //12行目
3. 2で割り出した値を.circleのtransform:scaleの値に指定
$(".circle").css({ "transform": "scale(" + sum + ")" }) // 15〜18行目
以上です。
少し細かく見ていきましょう。
考え方
1ではscrollTop()を用いてスクロール量を取得して変数に代入しています。
2ではcssで指定している円のスケールの初期値(1)に、スクロール量を50で割ったものを足した値を出しています。(この値で円のスケールを変更します)
50で割る理由ですが、そのままスクロール量の値を足してしまうと円があっという間に何十倍もの大きさになってしまうためです。
例えば、10px分のスクロール量がそのまま初期値に足された場合、下の図のように.circle{transform:scale(11)}となってしまいますね。(1 + 10 = 11倍)
10px分のスクロール量を50で割ってから初期値に足した場合、下の図のように.circle{transform:scale(1.2)}となるという考え方です。(1 + 10 / 50 = 1.2倍)
アニメーションをよりスムーズな動きに見せるため50で割っていると考えてください。
50の値を変更することでアニメーションの速度(変化の速さ)を調整できます。
値を大きくすることで緩やかに変化し、値を小さくすることで劇的に変化します。
3では2で割り出した値を、.circleのtransform:scale()の値に指定してスケールが変化するアニメーションを実行しています。
スクロールに合わせたマスクのサイズ変更
続いてはマスクのサイズ変更になります。
下記のコードで動作します。
※一般的なローカル環境やcode penでは、msakプロパティが動作しないため動作確認はこちらをご覧ください。
【HTML】
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>【jQuery】スクロールに合わせた要素のサイズ変更</title>
<link rel="stylesheet" href="sample.css">
</head>
<body>
<div class="mountain"></div>
<div class="content">
<h1>Content Start</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Mountain</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Sky</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Mountain</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Sky</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Mountain</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
<h2>About Sky</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="sample.js"></script>
</body>
</html>
【CSS】
@charset "UTF-8";
/*初期の余白やボックスの算出方法の指定*/
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
/*bodyの背景色と高さ(スクロールさせるため)の指定*/
body {
/*コンテンツの高さが十分ある場合heightの指定は不要*/
height: 5000px;
background-color: yellow;
}
/*マスク部分に対する指示*/
.mountain{
/*見た目や位置の調整*/
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: calc(50% - 25vh);
background-image: url(sample.jpg);
/*マスクの指定*/
mask-image: url("sample.svg");
mask-repeat: no-repeat;
mask-position: center top;
mask-size: 50%;
-webkit-mask-image: url("sample.svg");
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center top;
-webkit-mask-size: 50%;
z-index: 1;
}
/*コンテンツ部分に対する指示*/
.content{
width: 100vw;
height: 100vh;
padding: 50px;
background-color: #fff;
/*位置の指定 マスクアニメーションが終わってから表示されるよう*/
position: relative;
top: 200vh;
left: 0;
z-index: 2;
}
【JavaScript】
$(function () {
/* スクロール量 代入用 */
var scroll;
/* マスクの初期サイズ + スクロール量 代入用 */
var sum;
/* マスクの初期位置 - スクロール量 代入用 */
var top;
$(window).scroll(function () {
/* スクロール量を取得 */
scroll = $(this).scrollTop();
/* コンソールログで確認 */
console.log(scroll);
/* マスクの初期サイズ+スクロール量 / 10(調整)*/
sum = 50 + scroll / 10;
console.log(sum);
/* マスクの初期位置 + スクロール量 / 30(調整)*/
top = 25 + scroll / 30;
console.log(top);
$(".mountain").css({
/* マスクの大きさにスクロール量を足す */
"mask-size": sum + "%",
/* マスクの初期位置からスクロール量を引く */
"top": "calc(50% - " + top + "vh)"
})
/*マスクの初期サイズ+スクロール量が150を超えた場合*/
if(sum > 150){
/*非表示に*/
$(".mountain").fadeOut(500)
}else{
/*そうではない場合表示に*/
$(".mountain").fadeIn(500)
}
})
})
こちらは少々難しくなりますのでHTMLから細かくみていきましょう。
ファイル一式をこちらからダウンロードできるので、よろしければファイルを開きながらご覧ください!(一般的なローカル環境ではマスクが表示されないため、VS codeのlive serverや仮想サーバー環境などで動作確認をしてください)
HTMLの編集ポイント
divが二つあり.mountainが写真にマスクをかけている範囲、.contentがスクロール後に表示されるコンテンツの範囲となります。
コンテンツの範囲が不要な場合は.contentを削除してください。
CSSの編集ポイント
CSSは基本的にスタイルに関する記述となります。
.mountainに指定しているbackground-image(sample.jpg)をmask-image(sample.svg)でマスクしています。(26〜35行目)
2024年2月9日現在はEdgeに対してベンダープレフィックスが必要とされているため、-webkit-の記述も入っています。
マスク関連のプロパティに馴染みがない方は以下を参考にしてください。
プロパティ | 説明 | 値 | 値の説明 |
mask-image | 要素に適用するマスクの画像を指定します。 | none url(“mask.png”) linear-gradient(blue, green) | none: マスクを適用しません。 url(“mask.png”): mask.png という画像ファイルをマスクとして使用します。 linear-gradient(blue, green): 青から緑への線形グラデーションをマスクとして使用します。 |
mask-repeat | マスクの繰り返し方法を指定します。 | repeat no-repeat space round | repeat: マスクを繰り返します。 no-repeat: マスクを一度のみ表示します。 space: マスクを均等に配置しながら繰り返します。 round: マスクを画像のサイズに合わせて繰り返します。 |
mask-position | マスクの位置を指定します。 | left top center 20% 30% | left top: マスクの左上に配置します。 center: マスクを要素の中央に配置します。 20% 30%: マスクを横方向には左から20%、縦方向には上から30%の位置に配置します。 |
mask-size | マスクのサイズを指定します。 | auto cover 50% 50% | auto: マスクの元のサイズを保持します。 cover: マスクを要素全体に広げて表示します。 50% 50%: マスクの幅と高さを要素の50%にします。 |
スクロールの際にマスク領域が画面上から見切れてしまわないよう、position:fixedで固定をしています。(23行目)
またコンテンツ領域がスクロール後に表示されるようpositionで調整をしています。(46行目以降)
JavaScriptの編集ポイント
JavaScriptの考え方としては以下の通りとなります。
1. スクロール量を取得
scroll = $(this).scrollTop(); //10行目
2.マスクの初期サイズにスクロール量を10で割った値を足す
sum = 50 + scroll / 10; //14行目
3. マスクの初期位置からスクロール量を30で割った値を足す
top = 25 + scroll / 30; //17行目
4. 2で割り出した値を.mountainのmask-sizeの値に指定(単位は%)
"mask-size": sum + "%" //21行目
5. 3で割り出した値を.mountainのtopの値に指定(単位はvh)
"top": "calc(50% - " + top + "vh)" //23行目
以上です。
こちらも細かく見ていきましょう。
考え方
1ではscrollTop()を用いてスクロール量を取得して変数に代入しています。
2ではcssで指定しているマスクサイズの初期値(50)に、スクロール量を10で割ったものを足した値を出しています。
10で割る理由ですが、そのままスクロール量の値を足してしまうと.mountainがあっという間に何倍もの大きさになってしまうためです。
例えば、50px分のスクロール量がそのまま初期値に足された場合、下の図のように.mountain{mask-size:100%}となってしまいますね。(50 + 50 = 100%)
50px分のスクロール量を10で割ってから初期値に足した場合、下の図のように.mountain{mask-size:55%}となるという考え方です。(50 + 50 / 10 = 55%)
こちらもアニメーションをよりスムーズな動きに見せるため10で割っていると考えてください。
10の値を変更することでアニメーションの速度(変化の速さ)を調整できます。
値を大きくすることで緩やかに変化し、値を小さくすることで劇的に変化します。
3.マスクの位置を指定するための記述です。
transform:scale()の場合、中心から外に向かって大きくなりますが、mask-sizeプロパティは下方向に向かって大きくなるため、スクロールを続けると下の図のように見切れてしまいます。
マスク部分の領域を見切れさせないため、下の図のように位置を上にずらすことで要素の位置を調整しています。
ここではcssで指定している.mountain{top:calc(50% – 25vh)}の25(初期値)に対し、スクロール量を30で割ったものを足した値を出します。
30で割る理由ですが、そのままスクロール量の値を足してしまうと.mountainがあっという間に上へ移動をしてしまうためです。
30の値を変更することでアニメーションの速度(変化の速さ)を調整できます。
値を大きくすることで緩やかに移動し、値を小さくすることで素早く移動します。
4、5で.mountainのmask-sizeとtopの値に指定することで、マスクのスケールと位置が変化するアニメーションを実行しています。
一定スクロール後にマスクの表示・非表示を切り替えたい場合、26行目以降の記述を行います。
/*マスクの初期サイズ+スクロール量が150を超えた場合*/
if(sum > 150){
/*非表示に*/
$(".mountain").fadeOut(500)
}else{
/*そうではない場合表示に*/
$(".mountain").fadeIn(500)
}
上の記述でスクロール量が150pxを超えたらマスクが非表示になり、スクロール量が150px未満になったら表示になります。
以上となります!
少々複雑な解説になっていますが、実装ができるとワンランク上の表現ができると思います。
サンプルのコードを開きながら数値を調整していただくと分かりやすいはずですので、よろしければ是非活用してみてください!