Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Last active June 25, 2024 12:30
Show Gist options
  • Select an option

  • Save kobitoDevelopment/fb90195eaf297c1f83d805335b111690 to your computer and use it in GitHub Desktop.

Select an option

Save kobitoDevelopment/fb90195eaf297c1f83d805335b111690 to your computer and use it in GitHub Desktop.
<div class="parallax">
<div class="parallax__inner js-parallax">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</div>
<div class="parallax-end"></div>
/* スクロールアニメーションを設定 */
// スクロール率を取得
let scrollPercent = 0;
let animationID;
// 線形補完 第一引数:開始時 第二引数:終了時 第三引数:上限
function lerp(x, y, a) {
return (1 - a) * x + a * y;
}
function scalePercent(start, end) {
return (scrollPercent - start) / (end - start);
}
// アニメーション配列を設定
const animationScripts = [];
animationScripts.push(
{
start: 0, // アニメーションが開始される位置
end: 10, // アニメーションが終了する位置
function() {
parallaxContent.scrollTo({
left: 0,
});
},
},
{
start: 10, // アニメーションが開始される位置
end: 90, // アニメーションが終了する位置
function() {
parallaxContent.scrollTo({
left: lerp(0, parallaxContentWidth - windowWidth, scalePercent(this.start, this.end)),
});
},
},
{
start: 90, // アニメーションが開始される位置
end: 100, // アニメーションが終了する位置
function() {
parallaxContent.scrollTo({
left: parallaxContentWidth - windowWidth,
});
},
}
);
// アニメーション関数を設定
function playScrollAnimation() {
animationScripts.forEach(function (animation) {
if (scrollPercent >= animation.start && scrollPercent <= animation.end) {
animation.function();
}
});
}
//
const parallax = document.querySelector(".parallax");
const parallaxContent = document.querySelector(".js-parallax");
const parallaxEnd = document.querySelector(".parallax-end");
let scrollHeight;
const clientHeight = document.documentElement.clientHeight; // デバイス表示領域の高さを取得
let windowHeight;
let windowWidth;
let isIntersecting;
let setScrollTop;
let windowBottom;
let parallaxRect;
let parallaxTop;
let parallaxEndRect;
let parallaxEndTop;
let parallaxContentWidth;
function setSize() {
windowHeight = window.innerHeight;
windowWidth = window.innerWidth;
setScrollTop = window.pageYOffset || document.documentElement.scrollTop;
parallaxRect = parallax.getBoundingClientRect();
parallaxTop = parallaxRect.top + setScrollTop;
parallaxEndRect = parallaxEnd.getBoundingClientRect();
parallaxEndTop = parallaxEndRect.top + setScrollTop;
parallaxContentWidth = parallaxContent.scrollWidth; // 画面から溢れたコンテンツの長さも取得
parallax.style.height = parallaxContentWidth - windowWidth + "px";
}
window.addEventListener("load", function () {
setSize();
scrollHeight = parallax.offsetHeight; // パララックス要素の総スクロール量を取得
parallaxEndRect = parallaxEnd.getBoundingClientRect();
parallaxEndTop = parallaxEndRect.top + setScrollTop;
});
window.addEventListener("resize", function () {
setSize();
scrollHeight = parallax.offsetHeight; // パララックス要素の総スクロール量を取得
parallaxEndRect = parallaxEnd.getBoundingClientRect();
parallaxEndTop = parallaxEndRect.top + setScrollTop;
});
/* パララックス要素が画面内にある場合のみスクロールイベントを発火 開始 */
const observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
window.addEventListener("scroll", parallaxAction, { passive: true });
//画面内判定フラグ
isIntersecting = true;
} else {
window.removeEventListener("scroll", parallaxAction, { passive: true });
//画面内判定フラグ
isIntersecting = false;
}
});
});
//observer監視開始
observer.observe(parallax);
/* パララックス要素が画面内にある場合のみスクロールイベントを発火 終了 */
function parallaxAction() {
setSize();
scrollHeight = parallax.offsetHeight; // パララックス要素の総スクロール量を取得
parallaxEndRect = parallaxEnd.getBoundingClientRect();
parallaxEndTop = parallaxEndRect.top + setScrollTop;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollDif = scrollTop - parallaxTop; // スクロール量をアニメーション数値に利用したい場合に使用
scrollPercent = (scrollDif / (scrollHeight - clientHeight)) * 100; // 何%スクロールしているかを算出
windowBottom = scrollTop + windowHeight;
const controlClass = parallaxContent.classList; // classの着脱は変数に格納(performance配慮)
/* パララックス要素1つ目 */
if (scrollTop > parallaxTop && windowBottom > parallaxEndTop) {
parallaxContent.scrollTo({
left: parallaxContentWidth - windowWidth,
});
} else {
// パララックス開始位置を通過した場合
if (scrollTop > parallaxTop) {
playScrollAnimation();
} else {
// パララックス開始位置に到達する前
parallaxContent.scrollTo({
left: 0,
});
}
}
}
body {
padding-top: 50vh;
padding-bottom: 600px;
}
.parallax {
position: relative;
height: 100vh; // 希望の固定し続けたい高さ
width: 100%;
&__inner {
position: sticky;
top: 0;
left: 0;
height: 100vh;
width: 100%;
background: green; // デバッグ用背景
display: flex;
align-items: center;
gap: 6%;
overflow-x: auto;
-ms-overflow-style: none; //スクロールバー非表示 IE/Edgeへ対応
scrollbar-width: none; //スクロールバー非表示 firefoxへ対応
&::-webkit-scrollbar {
display: none; //スクロールバー非表示 safari/chromeへ対応
}
.box {
width: 30%; // デバッグ用boxサイズ
aspect-ratio: 1; // デバッグ用boxサイズ
flex-shrink: 0;
// デバッグ用
font-size: 240px;
color: white;
border: 4px solid #fff; // デバッグ用boxスタイル
&:focus {
border-color: red;
}
}
}
}
/*デバッグ用*/
.parallax-end {
margin-bottom: 50vh;
}
/*デバッグ用*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment