デモ
デモを読み込めませんでした。
仕様
- 用途
- ナビゲーション
- 動作
- DOM
- 種類
- レシピ
- 使うもの
- ScrollTrigger
- 動作確認
- three 0.186.0・gsap 3.15.0
- 公開日
しくみと調整
スクロールの向きだけを見て、下へ読み進めるあいだはヘッダーを上へ滑らせて隠し、少しでも上へ戻すと降ろします。向きは、スクロールのたびに前の位置と比べるのではなく、ScrollTrigger が持っている向きの値をそのまま読みます。
隠すときは 0.4 秒の power2.in、出すときは 0.45 秒の power3.out です。出すほうを強く減速させるのは、探し始めた人をできるだけ早く迎えるためで、隠すほうは気づかれない速さで足ります。動かす量はヘッダーの高さぶんで、下の縁の線まで画面の外へ出します。影を付けるなら、その影の分も足してください。
記事の冒頭を読んでいるあいだは隠しません。先頭でわずかに下へ動かしただけでヘッダーが消えると、ページが落ち着かなく見えるためです。冒頭の絵が画面の上へ抜けるところを、隠し始める線にしています。
向きが変わったときだけ、overwrite を付けたトゥイーンに置き換えます。スクロールのたびに作り直すと、前のトゥイーンと取り合ってヘッダーが小刻みに揺れます。メニューを開いているあいだや、ページ内のリンクで飛んだ直後は、出したまま止めておいてください。
長い記事・ドキュメント、画面の狭いスマートフォンのヘッダーに向いています。ヘッダーが画面の 5 分の 1 を占めるような場面ほど、返した場所が効きます。
コード
<header class="site-header">
<a class="site-header__logo" href="/">Hi! I'm HIGASHIYUKI®</a>
<nav class="site-header__nav" aria-label="カテゴリ">
<ul>
<li><a href="/culture/">Culture</a></li>
<li><a href="/technology/" aria-current="page">Technology</a></li>
<li><a href="/science/">Science</a></li>
<li><a href="/design/">Design</a></li>
</ul>
</nav>
<a class="site-header__cta" href="/subscribe/">購読する</a>
</header>
<article class="article">
<h1 class="article__title">The quiet return of the slow web</h1>
<!-- 冒頭の絵。ここが画面の上へ抜けるまでは、ヘッダーを隠さない -->
<img class="article-lead" src="/img/slow-web.jpg" alt="" width="1600" height="900">
<p>ポップアップが少なく、軽くて、最後まで読める文章。読む速さでWebを作り直す、小さな動きが広がっています。</p>
</article>
.site-header {
--ink: #eeece6;
--mute: #a8a6a0;
--ground: #111111;
position: fixed;
inset: 0 0 auto;
z-index: 10;
display: flex;
align-items: center;
gap: 32px;
box-sizing: border-box;
height: 112px;
padding: 0 6vw;
/* 地はページと同じ色。影は付けず、下の縁だけ罫線で区切る */
background-color: var(--ground);
border-bottom: 1px solid rgba(238, 236, 230, 0.14);
color: var(--mute);
font-size: 14px;
font-weight: 600;
letter-spacing: 0.04em;
white-space: nowrap;
}
.site-header a {
color: inherit;
text-decoration: none;
}
.site-header a:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 4px;
}
.site-header__logo {
flex: none;
margin-right: auto;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.12em;
color: var(--ink);
}
.site-header__nav ul {
display: flex;
gap: 32px;
margin: 0;
padding: 0;
list-style: none;
}
.site-header__nav [aria-current="page"] {
color: var(--ink);
}
.site-header__cta {
flex: none;
height: 40px;
padding: 0 24px;
border-radius: 999px;
background-color: var(--ink);
color: var(--ground);
line-height: 40px;
}
/* 本文の先頭は、固定したヘッダーの分だけ空ける */
.article {
max-width: 640px;
margin: 0 auto;
padding: 160px 6vw 144px;
}
.article__title {
margin: 0 0 24px;
font-size: clamp(28px, 3.4vw, 44px);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.02em;
}
.article-lead {
display: block;
width: 100%;
height: auto;
margin-bottom: 48px;
border-radius: 4px;
}
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const header = document.querySelector(".site-header");
const lead = document.querySelector(".article-lead");
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// 動きを減らす設定では、ヘッダーを出したままにする
if (header && !reduceMotion) {
let hidden = false;
const setHidden = (next) => {
// 向きが変わったときだけ作る。スクロールのたびに作ると、前のトゥイーンと取り合って揺れる
if (next === hidden) return;
hidden = next;
gsap.to(
header,
next
? // 下の縁の線まで画面の外へ出す(影を付けるなら、その分も足す)
{ y: -header.offsetHeight, duration: 0.4, ease: "power2.in", overwrite: true }
: { y: 0, duration: 0.45, ease: "power3.out", overwrite: true }
);
};
ScrollTrigger.create({
// 冒頭の絵が画面の上へ抜けるまでは隠さない(先頭からの量で決めるなら start: 100)
trigger: lead ?? document.body,
start: lead ? "bottom top" : 100,
end: "max",
onUpdate: (self) => setHidden(self.direction === 1), // 下へ送ると 1、上へ戻すと -1
onLeaveBack: () => setHidden(false), // 冒頭まで戻ったら出したまま
});
// メニューを開いているあいだや、ページ内リンクで飛んだ直後は setHidden(false) で出したままにする
}