HN 日本語サマリー

← 一覧へ戻る
Web開発

複数カラムでの異なるスクロール速度

Multi-Column Different-Speed Scrolling (blog.master.dev)

3 pointsby ibobev1 コメント

要約

この記事では、CSS Scroll-Driven Animationsを活用して、複数カラムのフォトギャラリーで各カラムのスクロール速度を異ならせたり、逆方向にスクロールさせたりするテクニックを紹介しています。JavaScriptを使わずに、CSSの`animation-timeline`プロパティを用いて視覚的に面白いスクロール効果を実現する方法を解説しています。

全文翻訳

複数カラムでの異なるスクロール速度

「ギャラリー」をスクロールしているとき、表示されるコンテンツがどのように流れていくかは、それほど重要ではないという考えが頭にありました。横方向にスクロールしても、異なる速度でスクロールしても、あるいは何らかの方法で逆方向にスクロールしても構いません。なぜなら、ユーザーは単に「探索」しているだけだからです。スクロールは新しいコンテンツを見るための単なるメカニズムに過ぎません。それでもスクロールバーは存在し、「あとどれくらい残っているか」という感覚は保たれるべきですが、それ以外はかなり自由な発想で良いのではないでしょうか?

異なる「速度」でのスクロール

私の最初のアイデアは、2つの写真カラムを異なる速度でスクロールさせることでした。最初のカラムは通常の速度でスクロールします。ページはスクロールするにつれて下に移動し、新しい写真が表示されていきます。2番目のカラムはより速く移動します。

スクロール自体を直接操作するのではなく、スクロールするにつれて値が変化する方向性のあるtransformを適用するというトリックを使います。

2つのカラム

まず、これらの効果を適用するためのカラムを用意する必要があります。そこで、簡単な2カラムグリッドをセットアップしましょう。

.photo-gallery {
  display: grid;
  grid-template-columns: repeat(2, minmax(150px, 1fr));
  align-items: start;
  gap: 1.5rem;
}

次に、その中に2つのカラムを設定できます。

<div class="photo-gallery">
  <div class="photo-column-left">
    <img ... />
    <img ... />
    ...
  </div>
  <div class="photo-column-right">
    <img ... />
    <img ... />
    ...
  </div>
</div>

ブレークポイントで2カラムレイアウトを中止することもできます。

.photo-gallery {
  ...

  @media (--small) {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}

あれ、メディアクエリとしてカスタムプロパティを使っている? その通りです。LightningCSSはこれを「将来のCSS」として処理できます。@custom-mediaを使って、クールな未来のように設定します。

@custom-media --small (width <= 500px);
右カラムをさらに速くスクロールさせる

前述したように、一方のカラム(右カラムとしましょう)をもう一方(左カラム)よりも速いレートでスクロールさせます。トリックは、スクロール時に適用する方向性のあるトランスフォームです。ここではScroll-Driven Animationsのscroll()バリアントを利用します(後でview()スタイルも試します)。Scroll-Driven Animationsのscroll()バリアントは、要素が現在どれだけスクロールされたかを判断するために使用できる数値を得る、より基本的な種類です。

.photo-column-right {
  /* ちょっとしたオフセット */
  padding-top: 80px;

  @supports (animation-timeline: scroll()) {
    @media (not (--small)) {
      animation-name: drift-up;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: scroll(root block); /* ページ全体 */
      animation-range: 0% 100%;
    }
  }
}

そこにある@supportsクエリに注意してください。すべてのブラウザがScroll-Driven Animationsをサポートしているわけではありません。それは anyway 構いません。これは単なるプログレッシブエンハンスメントの楽しみであり、重要な機能ではありません。

これで、次のように定義できる@keyframesアニメーションが呼び出され、魔法が起こります。

@keyframes drift-up {
  from {
    transform: translateY(12vh);
  }
  to {
    transform: translateY(-20vh);
  }
}

これらは少しマジックナンバー的です。状況に応じて適切な、やや任意の値です。追加の動きの量によっては、prefers-reduced-motionクエリも検討するかもしれません。

ボーナス:ズーム効果

単に面白半分で、各写真にview()スタイルのScroll-Driven Animationを使用できます。これにより、個々の写真がページを横切る間、元の形状を保ちながらズームして少し移動します。

.photo {
  overflow: clip;

  & > img {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
  }

  @supports (animation-timeline: view()) {
    & > img {
      animation-name: image-drift;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view(block);
      animation-range: cover 0% cover 100%;
    }
  }
}

これで、このimage-driftキーフレームアニメーションができます。好きなように設定できます。

@keyframes image-drift {
  from {
    scale: 1.5;
    translate: 0 -60px;
  }
  to {
    scale: 1;
    translate: 0 0;
  }
}
デモ

これにより、このようになります!

[画像: デモのスクリーンショット]

頭の中で思い描いていたことを正確に実現できたのは満足感があります。そして、JavaScriptは一切不要です。

さらに、写真はpopoverとして開くこともできます。なぜなら、最近のWebプラットフォームのクールな機能がもう一つ増えたからです。

リバーススクロール

もう一つ試してみたかったアイデアは、右カラムのスクロールを単に速くするのではなく、文字通り反転させることができるかどうかでした。本質的には、写真は上に伸びて(隠れたまま)から下に降りてくるようになりますが、スクロール方向は下向きのままです。少し奇妙ですが、まあいいでしょう。

もちろん、これを書きながらアイデアを試していると、Codropsが5年前に同様のことを行っていたことに気づきました。どうしようもないですね。しかし、彼らの実装はJavaScriptベースなので、これは異なる種類のアプローチです。

アプローチ

基本的なコアは同じで、ページがスクロールするにつれてカラムのtranslateYプロパティをアニメートします。しかし、それがうまくいくようにするには、もう少し工夫が必要です。例えば、右カラムを引き上げたいのですが、最初から写真が全く見えなくなるほど引き上げたくはありません。そのため、ビューポートの任意の高さで「最後の」画像が下端に接するのに必要なだけ引き上げ、そこからアニメートします。

その後、カラム全体にスティッキーポジショニングが適用され、その上の任意の位置にあるヘッダーがあっても均一にスクロールされるようになります。これがview()アニメーションと干渉するため、「画像ドリフト」はスクロール位置を使用して各画像のスケールとトランスフォームを個別に計算することで復元されます。これははるかにデリケートですが、行うのは楽しかったです。

デモ

[画像: リバーススクロールデモのスクリーンショット]

その他の楽しいもの!

CodePenで、さらに凝った他の写真ギャラリーの実装をいくつか見かけました!