💻

[CodePen解説] Blur Card with Parallax

2025/02/07に公開

CodePen では毎年、多くのクリエイターが公開するHTML/CSS/JSの作品が注目を集めます。
そのうち、2024年に特に人気の高かったHTMLスニペットをランキング形式でまとめた The Most Hearted of 2024 の作品を順番に解説していきます。
どの作品も個性的なアイデアや美しい表現が詰まっており、コードのポイントを押さえるだけでなく、実際に自分のプロジェクトへ活用するヒントにもつながるはずです。

最初のスニペットはこちら:HomeBlur Card with Parallax

概要

作りたいものは、「大阪の美しい景色を背景に、大きく"OSAKA"と文字が表示され、マウス(または指)の動きに合わせて背景や文字、建物などがほんの少し動いて奥行きを感じるアニメーションカード」 です。
さらに、その背景の一部をぼかす(blur)ことで、立体感やデザイン性を高めています。

  • パララックス効果:マウスの位置やスクロール位置などに応じて、前景・中景・背景の要素が異なる速度で動くアニメーション技法です。今回はマウス位置によって“ずれ”が生まれるように実装します。
  • ぼかし:CSSの filter: blur(...) で画像をぼかし、さらに mask で一部をグラデーション的に隠すことで、やわらかい雰囲気を演出します。

実装

HTML

1. <article>

  • 今回の“Blur Card”のメインコンテナ。中に複数の img を配置し、パララックスで動かしたり、ぼかしたりします。また、タイトルや補足テキスト等もここに格納します。

2. 各要素の重なりと動き

HTML構造の順序が、そのまま重なり順(Z軸方向)に反映されます。
(ただし z-index を設定しないかぎり、後から登場する要素が上に重なる)

  • 一番下: 1枚目の背景画像 (<img src="...sky.jpeg">)
  • 中央: タイトル(<h3>)+ 2枚目の建物画像 (<img src="...tower.png">)
  • その上: .blurコンテナ(ふわっとぼかしがかかった画像)
  • 最前面: .content(文字)
  • カード外に固定表示: .bear-link(クマアイコン、画面左上に固定)

2-1. レイヤーごとにパララックスを調整

HTML上では画像を1枚ずつ用意し、後ほどCSSで object-position: calc(-50% + (var(--x) * 30px)) などと記述することで、層ごとに移動量を変化させます。
こうすると、1枚目の背景と2枚目の建物画像とぼかし画像が別の速度・方向で動き、それぞれのパーツが重なりながら立体感を演出します。

2-2. ぼかし (.blur) の配置とテキスト

.blur 要素はカードの下部あたりに絶対配置され(inset: 60% 0 -26% 0; など)、強いぼかしやマスクを施しています。
.content はさらに前面にあり、テキストをしっかり見せたいのでブラーの影響は受けません。

3. blurクラス

  • もう1枚の画像をフィルタでぼかして、背景の一部を柔らかく表現するための要素。
  • CSSで filter: blur(20px); を付与する予定。

JavaScript

マウスやタッチの座標を取得し、**CSS変数(--x, --y)**に落とし込むことで、背景画像やテキストの位置を動かすパララックス演出を実現します。

1. GSAP ライブラリのインポート

import gsap from 'https://cdn.skypack.dev/gsap@3.12.0'
  • GSAP (GreenSock) は、JavaScriptで高度なアニメーションをシンプルに書ける人気ライブラリです。
  • 今回は上記のようにCDNから直接読み込み、gsap という変数を利用可能にしています。
  • GSAPには、要素のトランスフォーム・スタイルを調整するための便利メソッド(gsap.set など)や、数値を異なる範囲に変換するユーティリティ関数(gsap.utils.mapRange)が用意されています。これが、今回のパララックス効果実装を分かりやすくしてくれます。

2. UPDATE 関数 〜マウス位置をCSS変数へ〜

/**
 * マウスやタッチ移動時に呼び出される関数
 * イベント引数として { x, y } が渡される (pointermoveから)
 */
const UPDATE = ({ x, y }) => {
  gsap.set(document.documentElement, {
    // 画面幅(0 ~ innerWidth)を -1 ~ 1 に変換し、--x にセット
    '--x': gsap.utils.mapRange(0, window.innerWidth,  -1, 1, x),
    // 画面高さ(0 ~ innerHeight)を -1 ~ 1 に変換し、--y にセット
    '--y': gsap.utils.mapRange(0, window.innerHeight, -1, 1, y),
  })
}

(1) 引数 { x, y } について

  • pointermove イベントから渡されるオブジェクトには、マウスカーソルやタッチ位置の座標情報が入っています。
  • { x, y }e.x, e.y に相当するもので、画面の左上を基準として (0, 0) となり、右下側へ行くほど大きな数値になります。

(2) gsap.set(document.documentElement, { ... })

  • document.documentElement は、HTMLドキュメントの最上位要素(<html> タグ)です。
  • CSS変数(--x, --y)をHTML要素に直接セットすると、あらゆるCSSセレクタで var(--x), var(--y) を参照できるようになります。
  • これを使って、CSS側(たとえば object-position: calc(-50% + (var(--x) * 30px)) のような)で座標を自由に動かせます。

(3) gsap.utils.mapRange(...)

gsap.utils.mapRange(0, window.innerWidth, -1, 1, x)
  • mapRange は「ある数値を、ある範囲から別の範囲に線形変換する」ユーティリティ関数。
  • 0, window.innerWidth → 入力が 0(左端) 〜 innerWidth(右端)
  • -1, 1 → 出力を -1 〜 1 の範囲
  • x → 実際に変換したい値(マウスのX座標)
  • 例えば、マウスが画面左端にあるとき x=0 → 結果は -1 に近い値となり、中央にあるときは 0、右端なら +1 に近い値となります。
  • 同様に、縦方向の座標 y0 ~ window.innerHeight-1 ~ 1 に変換します。

3. pointermove イベントの登録

window.addEventListener('pointermove', UPDATE)
  • pointermove イベントは、マウスが動いたときだけでなく、タッチ操作(スマホやタブレットでの指の移動)なども捕捉できます。
  • ここでは画面全体(window) に対してイベントを登録しているので、どこでポインタを動かしても座標が検知されます。

イベントの流れ

  1. ユーザーが画面をなぞったり、マウスを動かす
  2. pointermove イベントが発火
  3. UPDATE 関数が呼ばれ { x, y } を受け取る
  4. --x, --y が新しい値に更新される
  5. → CSS側で var(--x), var(--y) を参照している部分(object-positiontranslate)が再計算され、要素が動く

こうすることで、HTMLやCSSに書いたvar(--x) / var(--y)が自動的に更新され、背景画像やテキストなど任意の要素をマウスの動きに合わせてアニメーションさせることができます。GSAP の mapRange によって座標を扱いやすい -1〜+1 の範囲に変換している点が、コードを分かりやすくしている大きなポイントです。

CSS

1. パララックス効果

パララックス効果を実現するためのカギは、object-positiontranslatevar(--x)var(--y) を使うことです。
これらのCSS変数の値は、JSでマウス移動に合わせて変化させます。
画像や文字の移動量(30px, 40px, -20px など)は異なる値を設定することで、層ごとに速度感が変わり、奥行きを感じられます。

画像のパララックス

article > img:first-of-type {
  filter: saturate(1.5) brightness(0.9);
  object-position: calc(-50% + (var(--x) * 30px))
                   calc(43% + (var(--y) * -20px));
}
  • object-position: calc(-50% + (var(--x) * 30px)) ...
  • object-position で背景画像の表示位置を決めています。
  • var(--x)-11 に変化するのに合わせて、 * 30px の分だけ画像が左右に動きます。
  • var(--y)-11 に変化するのに合わせて、 * -20px の分だけ画像が上下に動きます。
  • 画像そのものを微妙にズラすことで、マウス移動に応じて“揺れ”や“ズレ”が生じ、パララックスの奥行き感が出ます。

タイトル文字のパララックス

article h3 {
  position: absolute;
  left: 50%;
  top: 6%;
  font-size: 8rem;
  color: white;
  translate: calc(-50% + (var(--x) * -30px))
            calc(var(--y) * -20px);
}
  • translate: calc(-50% + (var(--x) * -30px)) ...
  • タイトル文字「OSAKA」にもパララックスを適用するため、translatevar(--x)var(--y) を掛け算しています。
  • 先ほどの背景画像と異なる係数を設定してあるので、動くスピード・方向に差が生まれ、より立体感が強調されます。

2. ぼかし (.blur)

filter: blur(20px) で大きめのぼかしをかけています。
さらに mask: radial-gradient(...) を使い、ぼかし画像の上辺をグラデーションで消しているのがポイント。
これにより、上部が自然に溶け込み、下半分がふんわりかかるように見えます。

ぼかし用のコンテナ

.blur {
  position: absolute;
  inset: 60% 0 -26% 0;
  filter: blur(20px);
  overflow: hidden;
}
  • filter: blur(20px);
  • 強めのぼかし効果をかけ、背景がにじむような見え方になります。
  • ここに画像を配置して、下部をぼんやりさせるのが狙いです。
  • inset: 60% 0 -26% 0;
  • カード内でどの範囲をぼかしの領域にするか、絶対配置で指定しています。
  • ここでは、カード下部あたり(“上から60%あたりまで”)がメインになるように計算。

ぼかし対象の画像

.blur img {
  object-position: calc(-50% + (var(--x) * 40px))
                   calc(47.5% + (var(--y) * -40px));
  mask: radial-gradient(50% 100% at 50% 90%, white 50%, transparent);
  filter: saturate(1.5) brightness(0.8);
}
  • object-position: ... (var(--x) * 40px)
  • ここでもパララックス効果を追加。(var(--x) * 40px) で背景がさらにズレるので、ほかの要素との速度差が生まれます。
  • mask: radial-gradient(50% 100% at 50% 90%, white 50%, transparent);
  • ぼかし画像の上端をグラデーションで消すためのテクニック。
  • 上部が自然にフェードアウトするので、背景と溶け合う表現がしやすくなります。

3. レスポンシブ対応

aspect-ratio でカードの縦横比を保ちつつ、max-width: calc(100% - 2rem) とすることで、画面サイズが小さい時にカードがはみ出さないようになっています。

カード本体 (article)

article {
  width: 600px;
  aspect-ratio: 2 / 1.1;
  min-height: 330px;
  position: relative;
  overflow: hidden;
  border-radius: 4em;
  max-width: calc(100% - 2rem);
}
  • aspect-ratio: 2 / 1.1;
  • 横:縦 = 2:1.1 の比率を維持したまま、要素がリサイズされます。
  • これによって、ウィンドウ幅が変化してもカードの縦横比が崩れにくいのがメリットです。
  • max-width: calc(100% - 2rem);
  • 親要素(画面)の100%に対して、マージン分の2remを差し引いています。
  • スマホなど小さい画面であってもカード全体がはみ出さずに収まるようにするための設定です。

まとめ

このように、HTMLでレイヤーを分け、CSSで位置とぼかしを制御し、JSで座標をCSS変数に変換する流れが、Blur Card with Parallax の基本でした。パラメータや画像、フィルタなどを自由に変えて、自分なりの美しい演出を作ってみても面白いかもしれません。

Discussion