スクロール
CSSとJSで作る、スクロールでサイバーな画面遷移が展開するヒーローセクション
2026/03/03
2026/2/28
ファーストビューを「ただの静止画像」で終わらせず、スクロールそのものを演出のスイッチにすると体験が一気に強くなります。
このデモは、最初にヒーロー背景とロゴを印象的に見せ、スクロールに合わせて説明文を露出させ、最後にギャラリーへ到達する流れ。
画面遷移はmask(斜めグラデーション)を使って、サイバーっぽい切り替えをCSS側で成立させています。
Preview プレビュー
Code コード
<section class="kumonosu-main-section">
<div class="kumonosu-wrapper-action">
<div class="kumonosu-hero" style="--degrade: -150deg; --transparent: 100%; --black: 100%;">
<picture>
<img src="https://images.unsplash.com/photo-1519608487953-e999c86e7455?q=80&w=1200" alt="cyber city" class="kumonosu-pixel-loader">
</picture>
<div class="kumonosu-pixel-overlay"></div>
<div class="kumonosu-hero-barcode-container">
<img src="https://kumonosu.net/wp-content/themes/kumonosu/assets/img/common/logo.svg" alt="LOGO" loading="lazy" width="300" height="auto">
</div>
</div>
<div class="kumonosu-content">
<div class="kumonosu-content-before" style="--transparent3: 100%; --black3: 100%;"></div>
<div class="kumonosu-content-after" style="--transparent2: 100%; --black2: 100%;">
<div class="kumonosu-title">
<h1>Friendly<br>Neighborhood</h1>
</div>
<div class="kumonosu-description">
<div>
<h2>KUMONOSU</h2>
<p>KUMONOSUはコピペで使えるCSS・JSのアニメーションデザインをまとめたギャラリーサイトです。 </p>
</div>
</div>
</div>
</div>
</div>
<div class="kumonosu-gallery-section" style="--transparent4: 100%; --black4: 100%;">
<div class="kumonosu-images">
<picture>
<img src="https://images.unsplash.com/photo-1550751827-4bd374c3f58b?q=80&w=1200" alt="cyber circuit" loading="lazy">
</picture>
<picture>
<img src="https://images.unsplash.com/photo-1620641788421-7a1c342ea42e?q=80&w=1200" alt="cyber neon street" loading="lazy">
</picture>
</div>
</div>
</section>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: #000;
color: #fff;
overflow-x: hidden;
}
.kumonosu-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.kumonosu-hero {
position: relative;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #000;
overflow: hidden;
mask: linear-gradient(var(--degrade), transparent var(--transparent), black var(--black));
-webkit-mask: linear-gradient(var(--degrade), transparent var(--transparent), black var(--black));
}
.kumonosu-hero picture {
position: relative;
display: block;
width: 100%;
height: 100%;
}
.kumonosu-hero picture img {
width: 100%;
height: 100%;
object-fit: cover;
}
.kumonosu-hero picture:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, 0.67) 94%);
z-index: 1;
}
.kumonosu-section {
position: relative;
width: 100%;
height: 100svh;
}
.kumonosu-pixel-loader {
width: 100%;
height: 100%;
object-fit: cover;
scale: 1.4;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
filter: contrast(0%) saturate(0%);
}
.kumonosu-pixel-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
repeating-linear-gradient(0deg,
transparent,
transparent 3px,
rgba(0, 0, 0, 0.3) 3px,
rgba(0, 0, 0, 0.3) 6px),
repeating-linear-gradient(90deg,
transparent,
transparent 3px,
rgba(0, 0, 0, 0.3) 3px,
rgba(0, 0, 0, 0.3) 6px);
mix-blend-mode: multiply;
opacity: 1;
}
.kumonosu-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
overflow: hidden;
}
.kumonosu-content-after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
mask: linear-gradient(var(--degrade2), transparent var(--transparent2), black var(--black2));
-webkit-mask: linear-gradient(var(--degrade2), transparent var(--transparent2), black var(--black2));
isolation: isolate;
}
.kumonosu-content-before {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120%;
height: 120%;
background: #1b1b30;
z-index: -1;
mask: linear-gradient(-105deg, transparent var(--transparent3), black var(--black3));
-webkit-mask: linear-gradient(-105deg, transparent var(--transparent3), black var(--black3));
}
.kumonosu-title {
width: 100%;
height: 80%;
display: flex;
align-items: center;
justify-content: center;
font-family: "Orbitron", sans-serif;
;
text-align: center;
}
.kumonosu-title h1 {
font-size: clamp(2rem, 8vw, 6rem);
}
.kumonosu-description {
width: 100%;
height: 20%;
padding-inline: 20px;
display: flex;
align-items: end;
padding-bottom: 20px;
}
.kumonosu-description div {
width: 50%;
}
.kumonosu-description div:nth-of-type(1) h2 {
font-size: clamp(2rem, 4vw, 3rem);
font-family: 'Montserrat', sans-serif;
font-weight: 600;
margin-bottom: 10px;
}
.kumonosu-description div:nth-of-type(1) p {
font-size: clamp(1rem, 2vw, 1rem);
font-family: 'Montserrat', sans-serif;
font-weight: 400;
}
.kumonosu-gallery-section {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 100vw;
height: 100svh;
background: #000;
z-index: 2;
mask: linear-gradient(105deg, transparent var(--transparent4), black var(--black4));
-webkit-mask: linear-gradient(105deg, transparent var(--transparent4), black var(--black4));
display: flex;
align-items: center;
justify-content: center;
}
.kumonosu-gallery-section .kumonosu-images {
width: 100%;
max-width: 1000px;
height: 70vh;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
padding: 0 20px;
}
.kumonosu-gallery-section .kumonosu-images picture {
flex: 1;
height: 100%;
overflow: hidden;
}
.kumonosu-gallery-section .kumonosu-images img {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0;
will-change: transform, opacity;
}
.kumonosu-hero-barcode-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
opacity: 1;
pointer-events: none;
will-change: transform, opacity, scale;
}
document.addEventListener('DOMContentLoaded', () => {
gsap.registerPlugin(ScrollTrigger);
const lenis = new Lenis();
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
const mainSection = document.querySelector('.kumonosu-wrapper-action');
const hero = document.querySelector('.kumonosu-hero');
const content = document.querySelector('.kumonosu-content-after');
const contentBefore = document.querySelector('.kumonosu-content-before');
const gallerySection = document.querySelector('.kumonosu-gallery-section');
const images = document.querySelectorAll('.kumonosu-gallery-section .kumonosu-images img');
const heroBarcodeContainer = document.querySelector('.kumonosu-hero-barcode-container');
// Initial setup animations
gsap.to(hero, {
'--black': '0%',
'--transparent': '0%',
'--degrade': '-90deg',
duration: 2,
ease: 'power2.out'
});
gsap.to('.kumonosu-pixel-loader', {
filter: 'contrast(100%) saturate(100%)',
scale: 1,
duration: 2,
ease: 'power2.out'
});
gsap.to('.kumonosu-pixel-overlay', {
opacity: 0,
duration: 2,
ease: 'power2.out',
delay: 1
});
// 自動で動くロゴのfromToアニメーションを削除しました
const mostrarGaleria = () => {
if (gallerySection.classList.contains('kumonosu-animated')) return;
gallerySection.classList.add('kumonosu-animated');
gsap.fromTo(images, {
x: 100,
opacity: 0
}, {
x: 0,
opacity: 1,
duration: 1.5,
stagger: 0.3,
ease: 'power2.out',
delay: 0.2
});
};
const ocultarGaleria = () => {
if (!gallerySection.classList.contains('kumonosu-animated')) return;
gallerySection.classList.remove('kumonosu-animated');
gsap.to(images, {
opacity: 0,
x: 100,
duration: 0.8,
stagger: 0.1,
ease: 'power2.in'
});
};
ScrollTrigger.create({
trigger: '.kumonosu-main-section',
start: 'top top',
end: `+=${window.innerHeight * 3}px`,
pin: true,
scrub: 1,
onUpdate: (self) => {
const progress = self.progress;
if (progress < 0.2) {
gsap.to(mainSection, {
scale: 1 - (progress * 0.8)
});
} else {
gsap.to(mainSection, {
scale: 0.8
});
}
// スクロール進行度に合わせてロゴを消す(スクロール操作のみに連動)
if (progress > 0.15) {
gsap.to(heroBarcodeContainer, {
opacity: 0,
duration: 0.5,
ease: 'power2.out'
});
} else {
gsap.to(heroBarcodeContainer, {
opacity: 1,
duration: 0.5,
ease: 'power2.out'
});
}
if (progress > 0.3) {
gsap.to(content, {
'--degrade2': '0deg',
'--transparent2': '0%',
'--black2': '0%',
duration: 1.5,
ease: 'power2.out'
});
} else {
gsap.to(content, {
'--degrade2': '-150deg',
'--transparent2': '100%',
'--black2': '100%',
duration: 1.5,
ease: 'power2.out'
});
}
if (progress > 0.6) {
gsap.to(contentBefore, {
'--transparent3': '0%',
'--black3': '0%',
duration: 2,
ease: 'power2.out'
});
} else {
gsap.to(contentBefore, {
'--transparent3': '100%',
'--black3': '100%',
duration: 2,
ease: 'power2.out'
});
}
if (progress > 0.85) {
gsap.to(gallerySection, {
'--transparent4': '0%',
'--black4': '0%',
duration: 2,
ease: 'power2.out'
});
} else {
gsap.to(gallerySection, {
'--transparent4': '100%',
'--black4': '100%',
duration: 2,
ease: 'power2.out'
});
}
if (progress > 0.9) {
mostrarGaleria();
} else {
ocultarGaleria();
}
},
});
});
https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js
https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js
https://unpkg.com/lenis@1.1.20/dist/lenis.min.js
Explanation 詳しい説明
仕様
このデモは、ヒーロー画像を“スキャンしていく”ように見せながら、スクロールに合わせてコンテンツとギャラリーへ段階的に遷移する構成です。maskの角度と透明度をGSAPで操作し、画面が斜めに開閉するサイバー演出を作っています。
・ページ読み込み時にヒーローのマスクを開き、背景画像のコントラストと彩度を戻す
・ピクセル風オーバーレイをフェードアウトさせ、スキャン完了のような印象を演出
・ロゴはスクロール進行度に応じてフェードアウトし、視線を下のコンテンツへ誘導
・セクション全体をpinして1画面内で演出を展開
・進行度0.3以降でタイトル・説明ブロックのマスクを解除
・進行度0.6以降で中間背景レイヤーを表示
・進行度0.85以降でギャラリーセクションのマスクを解除
・進行度0.9以降でギャラリー画像をスライド+フェード表示
カスタム
演出の印象は、マスク角度と進行タイミングで大きく変わります。スクロールの長さや切り替えの速度を調整することで、よりドラマチックにも、よりテンポよくも仕上げられます。
・演出の区切り位置はprogressの数値(0.15 / 0.3 / 0.6 / 0.85 など)で調整
・斜めに開く向きは--degradeや--degrade2の角度を変更
・スキャン感を強めたい場合はpixel-overlayの不透明度や背景filterを調整
・全体の演出時間はwindow.innerHeight * 3の倍率を変更
・ギャラリーの出現アニメーションはdurationとstaggerで調整
・ヒーロー縮小率はscaleの計算式を変更してコントロール
注意点
スクロール中に複数のgsap.to()を実行しているため、端末性能によっては負荷が出る可能性があります。また、maskやfilterはブラウザ差が出やすい表現です。
・onUpdate内でアニメーションを頻繁に更新しているため負荷がかかる場合がある
・安定性を高めるならScrollTriggerのtimeline化がおすすめ
・maskは環境によって見え方が変わる可能性がある
・強いfilterやscaleは低スペック端末で重くなることがある
・Lenisは他のスクロール制御と干渉する可能性がある