lab-css

読了の灯

文章を読み進めると和蝋燭が燃え進む読了プログレス。scroll-timelineとanimation-timelineだけでスクロール量と蝋燭の長さを同期させる。

全画面で見る
Scroll-driven Animations操作: スクロール

文章をスクロールすると蝋燭が燃え進みます

方丈記

ゆく河の流れは絶えずして、しかももとの水にあらず。よどみに浮かぶうたかたは、かつ消えかつ結びて、久しくとどまりたるためしなし。世の中にある人とすみかと、またかくのごとし。

たましきの都のうちに、棟を並べ、甍を争へる、高き、卑しき、人の住まひは、世々を経て尽きせぬものなれど、これをまことかと尋ぬれば、昔ありし家はまれなり。或いは去年焼けて今年作れり。或いは大家滅びて小家となる。住む人もこれに同じ。所も変はらず、人も多かれど、いにしへ見し人は、二、三十人が中に、わづかにひとりふたりなり。朝に死に、夕べに生まるるならひ、ただ水の泡にぞ似たりける。

知らず、生まれ死ぬる人、いづかたより来たりて、いづかたへか去る。また知らず、仮の宿り、誰がためにか心を悩まし、何によりてか目を喜ばしむる。その、主人と栖と、無常を争ふさま、いはばあさがほの露に異ならず。

或いは露落ちて花残れり。残るといへども朝日に枯れぬ。或いは花しぼみて露なほ消えず。消えずといへども夕べを待つことなし。

ここまで読み進めたぶんだけ、右の蝋燭は短くなっています。スクロール位置と蝋燭の長さは、scroll-timelineとanimation-timelineだけで同期しています。

(了)

お使いのブラウザはスクロール駆動アニメーションに未対応のため、蝋燭は燃え進みません。

対応ブラウザ

CSS機能Chrome / EdgeFirefoxSafari備考
Scroll-driven Animations115+未対応26+Firefoxはフォールバック表示

コード

表示中のコードは作品の実行に使われているものと同一ソースです。そのままコピーして試せます。

/* 読了の灯: 文章のスクロール量に同期して和蝋燭が燃え進む。
   scroll-timelineとanimation-timelineのみで実現(JS不使用) */

.work-scroll-progress {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background:
    radial-gradient(70% 60% at 78% 30%, rgba(255, 176, 92, 0.08), transparent 65%),
    #100d0a;
  /* スクローラーの外にいる蝋燭からタイムラインを参照できるようにする */
  timeline-scope: --sp-read;
}

/* ---- 本文スクロール領域(このスクロールがタイムラインになる) ---- */

.sp-scroller {
  position: absolute;
  inset: 0;
  padding-right: 130px;
  overflow-y: scroll;
  scroll-timeline: --sp-read block;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 200, 140, 0.25) transparent;
}

.sp-article {
  display: flex;
  flex-direction: column;
  max-width: 34em;
  /* 器が大きくても必ずスクロールが発生するよう最低の高さを確保する */
  min-height: 185%;
  margin: 0 auto;
  padding: 36px 24px 48px;
  color: rgba(240, 230, 214, 0.82);
  font-size: 15px;
  line-height: 2.3;
}

.sp-title {
  margin: 0 0 28px;
  color: #f0ddb8;
  font-size: 22px;
  letter-spacing: 0.5em;
  text-align: center;
}

.sp-article p {
  margin: 0 0 2em;
  text-indent: 1em;
}

.sp-note {
  margin-top: 3em;
  color: rgba(240, 200, 140, 0.55);
  font-size: 12.5px;
}

.sp-end {
  margin-top: auto;
  color: rgba(240, 230, 214, 0.45);
  text-align: right;
}

/* ---- 和蝋燭 ---- */

.sp-candle {
  position: absolute;
  top: 24px;
  bottom: 24px;
  right: 34px;
  width: 56px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  pointer-events: none;
}

/* 蝋: 高さがスクロール量に応じて縮む。炎はflex配置なので自然に降りてくる */
.sp-wax {
  width: 20px;
  height: 58%;
  border-radius: 8px 8px 4px 4px;
  background:
    repeating-linear-gradient(0deg,
      rgba(150, 110, 60, 0.12) 0 3px, transparent 3px 26px),
    linear-gradient(90deg,
      #e6d3b0 0%, #f7ecd2 45%, #d8c194 100%);
  box-shadow: inset 0 6px 8px rgba(120, 80, 30, 0.3);
  animation: sp-burn linear both;
  animation-timeline: --sp-read;
}

@keyframes sp-burn {
  from { height: 58%; }
  to { height: 7%; }
}

.sp-flame {
  position: relative;
  width: 16px;
  height: 26px;
  margin-bottom: 2px;
  border-radius: 50% 50% 45% 45% / 62% 62% 38% 38%;
  background: radial-gradient(circle at 50% 78%,
    #fff9e8 0 22%, #ffd166 45%, #ff8c42 75%, rgba(255, 100, 40, 0) 100%);
  filter: drop-shadow(0 0 14px rgba(255, 170, 80, 0.75));
  animation: sp-flicker 1.6s ease-in-out infinite;
}

/* 芯 */
.sp-flame::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -4px;
  width: 2px;
  height: 6px;
  translate: -50%;
  background: #4a3320;
}

@keyframes sp-flicker {
  0%, 100% { transform: scaleY(1) rotate(-1.5deg); }
  30% { transform: scaleY(1.08) rotate(1.5deg); }
  60% { transform: scaleY(0.94) rotate(-1deg); }
}

.sp-dish {
  width: 48px;
  height: 10px;
  margin-top: 4px;
  border-radius: 4px 4px 10px 10px;
  background: linear-gradient(180deg, #6b4a2b, #3c2716);
}

/* ---- スクロール駆動アニメーション非対応ブラウザへの告知 ---- */

@supports not (animation-timeline: scroll()) {
  .work-scroll-progress .work-warn {
    display: block;
  }
}

/* 炎の揺らぎのみ時間アニメーションなので、動きへの配慮で停止する。
   蝋の燃え進みはスクロール駆動(ユーザー操作起点)のため残す */
@media (prefers-reduced-motion: reduce) {
  .work-scroll-progress .sp-flame {
    animation-play-state: paused;
  }
}