CSSとJSで作る、スクロールでサイバーな画面遷移が展開するヒーローセクション

スクロール

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の倍率を変更
・ギャラリーの出現アニメーションはdurationstaggerで調整
・ヒーロー縮小率はscaleの計算式を変更してコントロール

注意点

スクロール中に複数のgsap.to()を実行しているため、端末性能によっては負荷が出る可能性があります。また、maskfilterはブラウザ差が出やすい表現です。

onUpdate内でアニメーションを頻繁に更新しているため負荷がかかる場合がある
・安定性を高めるならScrollTriggerのtimeline化がおすすめ
maskは環境によって見え方が変わる可能性がある
・強いfilterscaleは低スペック端末で重くなることがある
・Lenisは他のスクロール制御と干渉する可能性がある