GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

リキッドガラス デザイン 9パターン — 屈折計算で作るコピペ対応

リキッドガラス デザインは、板の縁だけを歪ませる屈折計算ひとつから生まれます。03番の縁の色分離も、04番の弾性戻りも、08番の押し込み厚み変化も、すべて同じ屈折計算ひとつから枝分かれした結果だ。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

リキッドガラス デザイン 9パターン — 屈折計算ひとつが奥行きをどう変えるか

九つのパネルはすべて、あるピクセルがパネル自身の縁からどれだけ近いかを読み取り、その分だけ背景を内側へ押し込みます。01番はその縁の値ひとつに強さの定数を掛けるだけで止まります。02番から07番までは同じ縁の計算の上に、ハイライトの位置・チャンネルごとのずれ幅・減衰時間・輝度の読み取り・描画順・距離による波のどれか一つだけを重ねます。08番は同じ計算を平らな板ではなく丸いドームに適用し、09番はスクロールしている間だけ反応する横長の帯にそれを使い回します。以下の並びは、裸の計算から、それが運べるすべての値へと進みます。

01縁だけが背景を歪ませる屈折レンズ

パネルの中央は背景がそのまま見えていて、外側の縁の厚みの分だけ同じ背景が内側へ歪んで見えます。GLASS_REFRACTION_STRENGTH は boxEdgeSample() が返す縁の係数をどれだけ強く押し込むかを決めていて、この一つの値を上げるだけで中央には触れずに縁の歪みだけが深くなります。

縁だけが背景を歪ませる屈折レンズ
新しいタブで開く

01 · 縁だけが背景を歪ませる屈折レンズ — 計測 累積面積 23.072% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

GLASS_REFRACTION_STRENGTHboxEdgeSamplepanX
  var PANEL_X0 = 20, PANEL_Y0 = 16, PANEL_X1 = 140, PANEL_Y1 = 84;
  var EDGE_BAND = 16;
  var GLASS_REFRACTION_STRENGTH = 8;
  var SCENE_OFFSET_X = (SCENE_W - CALC_W) / 2;
  var PAN_AMP = 34;
  var PERIOD_MS = 2000;

  function boxEdgeSample(x, y, x0, y0, x1, y1, band) {
    var dl = x - x0, dr = x1 - x, dt = y - y0, db = y1 - y;
    var dmin = Math.min(dl, dr, dt, db);
    if (dmin >= band || dmin < 0) return { f: 0, nx: 0, ny: 0 };
    var nx = 0, ny = 0;
    if (dl < band) nx -= (band - dl);
    if (dr < band) nx += (band - dr);
    if (dt < band) ny -= (band - dt);
    if (db < band) ny += (band - db);
    var len = Math.sqrt(nx * nx + ny * ny);
    if (len > 0.0001) { nx /= len; ny /= len; }
    return { f: Math.max(0, (band - dmin) / band), nx: nx, ny: ny };
  }

実測: 累積面積23.1%・強度40.5・23/23 フレームが動きました。

02ポインターを追う反射ハイライト

ポインターがパネルの上にある間はその位置に合わせて明るい反射の帯が動き、離れると同じ対角線上をひとりでに行き来します。virtualCenter() がその対角線上の位置を行き来させ、HIGHLIGHT_BAND が Math.exp(-diff*diff) による帯の幅を決めていて、下にある歪み自体は01番と同じ boxEdgeSample() の縁の値がそのまま使われています。

ポインターを追う反射ハイライト
新しいタブで開く

02 · ポインターを追う反射ハイライト — 計測 累積面積 39.642% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

virtualCenterpointerDiagHIGHLIGHT_BAND
  function renderFrame(t) {
    var center = pointerActive ? pointerDiag : virtualCenter(t);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var rgb;
        if (x >= PANEL_X0 && x <= PANEL_X1 && y >= PANEL_Y0 && y <= PANEL_Y1) {
          var e = boxEdgeSample(x, y, PANEL_X0, PANEL_Y0, PANEL_X1, PANEL_Y1, EDGE_BAND);
          var mag = e.f * GLASS_EDGE_BEND;
          rgb = sampleScene(x - e.nx * mag, y - e.ny * mag);
          var d = ((x - PANEL_X0) + (y - PANEL_Y0)) / DIAG_SPAN;
          var diff = (d - center) / HIGHLIGHT_BAND;
          var shine = Math.exp(-diff * diff) * HIGHLIGHT_MAX;
          rgb = [Math.min(255, rgb[0] + shine), Math.min(255, rgb[1] + shine), Math.min(255, rgb[2] + shine)];

実測: 累積面積39.6%・強度57.7・20/23 フレームが動きました。

03縁で色が分かれて見えるガラス

パネルの縁に近づくほど、背景の赤・緑・青がわずかにずれて重なって見えます。GLASS_CHROMA_SHIFT_PX は GLASS_BASE_BEND による縁の歪みの上に、三つの色チャンネルをどれだけ離してサンプリングするかを決めていて、この値だけを上げると色のずれが目立つほど広がります。

縁で色が分かれて見えるガラス
新しいタブで開く

03 · 縁で色が分かれて見えるガラス — 計測 累積面積 51.8% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

GLASS_CHROMA_SHIFT_PXsampleChannelboxEdgeSample
  function renderFrame(t) {
    var pan = panX(t);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var baseSx = x + SCENE_OFFSET_X + pan;
        if (x >= PANEL_X0 && x <= PANEL_X1 && y >= PANEL_Y0 && y <= PANEL_Y1) {
          var e = boxEdgeSample(x, y, PANEL_X0, PANEL_Y0, PANEL_X1, PANEL_Y1, EDGE_BAND);
          var mag = e.f * GLASS_BASE_BEND;
          var chroma = e.f * GLASS_CHROMA_SHIFT_PX * 6;
          var rC = sampleChannel(baseSx - e.nx * (mag + chroma), y - e.ny * (mag + chroma), 0);
          var gC = sampleChannel(baseSx - e.nx * mag, y - e.ny * mag, 1);
          var bC = sampleChannel(baseSx - e.nx * (mag - chroma), y - e.ny * (mag - chroma), 2);
          outBuf[idx] = rC; outBuf[idx + 1] = gC; outBuf[idx + 2] = bC;
        } else {
          outBuf[idx] = sampleChannel(baseSx, y, 0);
          outBuf[idx + 1] = sampleChannel(baseSx, y, 1);
          outBuf[idx + 2] = sampleChannel(baseSx, y, 2);

実測: 累積面積51.8%・強度92.3・23/23 フレームが動きました。

04引っ張ると伸びて離すとばねのように揺れ戻るガラス

角をつかんで引っ張っている間は三次イージングで目標地点まで伸び、離した瞬間から elasticSpring(rt) が GLASS_ELASTIC_RETURN_MS を時定数とする指数減衰と GLASS_ELASTIC_OSC_MS を振動周期とする Math.cos 項を掛け合わせて値を戻します。単一の減衰曲線ではなくアンダーダンプのばねなので、離した後2〜3回オーバーシュートしながら揺れて0へ収束し、この値を大きくしてもオーバーシュートの回数ではなく収束までの時間だけが伸びます。

引っ張ると伸びて弾性で戻るガラス
新しいタブで開く

04 · 引っ張ると伸びて弾性で戻るガラス — 計測 累積面積 31.812% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

GLASS_ELASTIC_RETURN_MScurrentOffsetvirtualOffset
  function elasticSpring(rt) {
    var decay = Math.exp(-rt / GLASS_ELASTIC_RETURN_MS);
    var osc = Math.cos((2 * Math.PI / GLASS_ELASTIC_OSC_MS) * rt);
    return decay * osc;
  }

  function virtualOffset(t) {
    var cyc = t % PERIOD_MS;
    if (cyc < DRAG_PHASE_MS) {
      var p = cyc / DRAG_PHASE_MS;
      var e = 1 - Math.pow(1 - p, 3);
      return { x: e * DRAG_MAX_X, y: e * DRAG_MAX_Y };
    }
    var rt = cyc - DRAG_PHASE_MS;
    var k = elasticSpring(rt);
    return { x: DRAG_MAX_X * k, y: DRAG_MAX_Y * k };
  }

実測: 累積面積31.8%・強度27.7・20/23 フレームが動きました。

05背景の明るさでティントが変わるガラス

パネルの内側では、背景が明るいところはティントが薄くなり、背景が暗いところは同じティントが濃くなります。TINT_STRENGTH は基準の明るさとサンプリングした輝度との差を調整する係数で、01番と同じ boxEdgeSample() の縁の歪みの上にこの色の変化が重なります。

背景の明るさでティントが変わるガラス
新しいタブで開く

05 · 背景の明るさでティントが変わるガラス — 計測 累積面積 74.724% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

TINT_STRENGTHtriangleWaveboxEdgeSample
  function renderFrame(t) {
    var pan = panX(t);
    var adaptivity = triangleWave(t, PERIOD_MS);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var baseSx = x + SCENE_OFFSET_X + pan;
        var rgb;
        if (x >= PANEL_X0 && x <= PANEL_X1 && y >= PANEL_Y0 && y <= PANEL_Y1) {
          var e = boxEdgeSample(x, y, PANEL_X0, PANEL_Y0, PANEL_X1, PANEL_Y1, EDGE_BAND);
          var bendMag = e.f * GLASS_EDGE_BEND;
          rgb = sampleScene(baseSx - e.nx * bendMag, y - e.ny * bendMag);
          var lum = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
          var delta = (128 - lum) * TINT_STRENGTH * adaptivity * 0.5;
          rgb = [rgb[0] + delta, rgb[1] + delta * 0.94, rgb[2] + delta * 0.86];
          var shine = e.f * 18;
          rgb = [
            Math.max(0, Math.min(255, rgb[0] + shine)),
            Math.max(0, Math.min(255, rgb[1] + shine)),
            Math.max(0, Math.min(255, rgb[2] + shine))

実測: 累積面積74.7%・強度30.6・23/23 フレームが動きました。

06重なる順番が回るほど奥行きが現れるガラス板

三枚のパネルの屈折・ぼかし・影は固定値ではなく、depthFor() が毎フレーム計算する深度インデックス(手前が0→奥が2)に比例して決まります — 屈折は BEND_PER_DEPTH の分だけ、ぼかしは BLUR_RADIUS_PER_DEPTH の分だけ深度が深いほど大きくなり、影の帯の大きさ・濃さも同時に深くなります。stackOrder() が3等分の周期で前後の順番を入れ替えるときは、値が瞬間的に飛ばないよう STACK_TRANSITION_MS かけて前の深度から新しい深度へなだらかに移行します。

重なるほど奥行きが現れるガラス板
新しいタブで開く

06 · 重なるほど奥行きが現れるガラス板 — 計測 累積面積 37.909% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

stackOrderdarkenBandPANELS
  function orderForSlot(slot) {
    slot = ((slot % 3) + 3) % 3;
    if (slot === 0) return [0, 1, 2];
    if (slot === 1) return [1, 2, 0];
    return [2, 0, 1];
  }

  function stackOrder(t) {
    return orderForSlot(Math.floor((t % PERIOD_MS) / SLOT_MS));
  }

  function depthFor(panelIdx, t) {
    var cyc = t % PERIOD_MS;
    var slot = Math.floor(cyc / SLOT_MS);
    var tSlot = cyc - slot * SLOT_MS;
    var curOrder = orderForSlot(slot);
    var prevOrder = orderForSlot(slot - 1);
    var curDepth = (PANELS.length - 1) - curOrder.indexOf(panelIdx);
    var prevDepth = (PANELS.length - 1) - prevOrder.indexOf(panelIdx);
    var blend = Math.min(1, tSlot / STACK_TRANSITION_MS);
    var e = 1 - Math.pow(1 - blend, 2);
    return prevDepth + (curDepth - prevDepth) * e;
  }

実測: 累積面積37.9%・強度30.8・23/23 フレームが動きました。

07押した場所から表面屈折が波状に広がるガラス

押した地点を中心に表面の屈折が輪になって外側へ広がり、縁に届く前に収まっていきます。RIPPLE_AMPLITUDE は envelope(年齢による指数減衰と距離による減衰を掛け合わせたもの)がゼロへ落とし込む波の振幅を決めていて、そのずれは01番と同じ縁の歪みの上に足し込まれます。

押した場所から表面屈折が波状に広がるガラス
新しいタブで開く

07 · 押した場所から表面屈折が波状に広がるガラス — 計測 累積面積 26.01% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

activePressPRESS_SCHEDULERIPPLE_AMPLITUDE
  function renderFrame(t) {
    var press = activePress(t);
    var age = Math.max(0, t - press.t);
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var rgb;
        if (x >= PANEL_X0 && x <= PANEL_X1 && y >= PANEL_Y0 && y <= PANEL_Y1) {
          var e = boxEdgeSample(x, y, PANEL_X0, PANEL_Y0, PANEL_X1, PANEL_Y1, EDGE_BAND);
          var dx = x - press.x, dy = y - press.y;
          var dist = Math.sqrt(dx * dx + dy * dy) + 0.0001;
          var envelope = Math.exp(-age / 260) * Math.max(0, 1 - dist / 95);
          var wave = Math.sin(dist * 0.3 - age * 0.02) * envelope * RIPPLE_AMPLITUDE;
          var nx = dx / dist, ny = dy / dist;
          var offX = e.nx * (e.f * GLASS_EDGE_BEND) + nx * wave;
          var offY = e.ny * (e.f * GLASS_EDGE_BEND) + ny * wave;
          rgb = sampleScene(x - offX, y - offY);
          var shine = e.f * 16 + Math.max(0, wave) * 8;
          rgb = [Math.min(255, rgb[0] + shine), Math.min(255, rgb[1] + shine), Math.min(255, rgb[2] + shine)];
        } else {

実測: 累積面積26.0%・強度22.2・23/23 フレームが動きました。

08押し込んだ厚みぶんだけ膨らみが減るボタン

ボタンは普段は背景を膨らませて見せていて、押し込まれると同じ分だけ厚みが薄くなり膨らみも縮みます。squeeze は押すと SQUEEZE_MIN に近づき、ドームの高さ・サンプリング位置のずれ・縁の明るさと持ち上がりをまとめて一つの値で薄くします。

押し込んだ厚みぶんだけ膨らみが減るボタン
新しいタブで開く

08 · 押し込んだ厚みぶんだけ膨らみが減るボタン — 計測 累積面積 14.814% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

wantPressedSQUEEZE_MINvirtualPressed
    var k = 1 - squeeze;
    var pressY = BTN_CY + (1 - squeeze) * 3;
    var bandStart = BAND_BASE - k * BAND_K;
    var shadowMult = SHADOW_MULT_BASE + k * SHADOW_MULT_K;
    var haloMult = HALO_MULT_BASE + k * HALO_MULT_K;
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var rgb;
        var nx = (x - BTN_CX) / BTN_HW;
        var ny = (y - pressY) / BTN_HH;
        var r2 = nx * nx + ny * ny;
        if (r2 <= 1) {
          var dome = (1 - r2) * BULGE_STRENGTH * squeeze;
          var sx = x - nx * BTN_HW * dome * 0.5;
          var sy = y - ny * BTN_HH * dome * 0.5;
          rgb = sampleScene(sx, sy);
          var rNorm = Math.sqrt(r2);
          var rim = Math.max(0, (rNorm - bandStart) / (1 - bandStart)) * RIM_MAX * squeeze;
          var lift = (1 - r2) * LIFT_MAX * squeeze;

実測: 累積面積14.8%・強度29.7・23/23 フレームが動きました。

09スクロール値に反応する上部ガラスナビゲーション場面

下の場面をスクロールするほど、上部のガラスナビゲーションバーはより厚く、より曇って見えるようになります。EXTRA_BEND は固定の BASE_BEND の上にスクロールの進み具合(progress)を掛けて重なり、同じ進み具合が曇りの混ざり具合とバーに沿った波を一度に動かします。

スクロール値に反応する上部ガラスナビゲーション場面
新しいタブで開く

09 · スクロール値に反応する上部ガラスナビゲーション場面 — 計測 累積面積 74.716% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

wantProgressEXTRA_BENDtriangleWave
  function renderFrame(t, progress) {
    var panY = progress * PAN_RANGE;
    var idx = 0;
    for (var y = 0; y < CALC_H; y += 1) {
      for (var x = 0; x < CALC_W; x += 1) {
        var rgb;
        if (y >= NAV_Y0 && y <= NAV_Y1) {
          var e = boxEdgeSample(x, y, NAV_Y0 - 999, NAV_Y0, NAV_Y0 + 1998, NAV_Y1, EDGE_BAND);
          var bendMag = (BASE_BEND + EXTRA_BEND * progress) * (e.f || 1) * 0.6 + BASE_BEND * 0.4;
          var ripple = Math.sin(x * 0.16 - t * 0.006) * RIPPLE_AMP * progress;
          rgb = sampleScene(x, (y + panY) - e.ny * bendMag - ripple);
          var frost = 60 * progress;
          rgb = [
            Math.min(255, rgb[0] * (1 - progress * 0.25) + frost),
            Math.min(255, rgb[1] * (1 - progress * 0.25) + frost),
            Math.min(255, rgb[2] * (1 - progress * 0.25) + frost)
          ];
        } else {

実測: 累積面積74.7%・強度45.4・23/23 フレームが動きました。

実務のレイアウトでリキッドガラス パネルが選ばれる場面

01番の縁だけの歪みはヒーローセクションのガラスカードに向いていて、周囲の場面がそのまま見えつつカードの縁だけがガラスらしく主張します。02番のポインター追従ハイライトは商品紹介のガラスパネルに合っていて、静止したハイライトでは訪問者のカーソルに反応していないことがすぐ分かってしまいます。03番の色分離は設定モーダルのガラス縁として使うと、少しの色ずれがレンズらしい上質さとして読めます。04番の弾性ドラッグは実際に動かすドラッグ可能なウィジェットウィンドウに、05番の輝度適応ティントはダーク・ライト両対応のナビゲーションバーに向いています。

css リキッドグラス ジェネレーターは数秒でぼかしだけの背景を作れますが、上の六つの用途はどれもカバーできません — ジェネレーターには読み取る場面も、押した・引っ張ったという状態もないからです。06番の奥行きはカード数枚を重ねたギャラリーに、07番の波紋は押した感触を伝えたい決済確認ボタンに向いています。08番の押し込みはリキッドグラス ボタンとして送信ボタンにそのまま使え、09番のスクロール連動はサイト上部の固定ナビゲーション以外の場所には向きません。スクロールする対象がなければ、値を動かすものが何もないからです。

値が屈折の度合いを変える場所 — 屈折強度・色分離幅・弾性戻り時間

九つのうち三つだけは、ファイル内の他の部分に触れずに一つの定数だけを動かせるようになっていて、下の表は実際にその定数を両端まで振り切って測った結果です。

バリエーション 値 感触
屈折強度の値を調整する 01 GLASS_REFRACTION_STRENGTH 8 → 28 値を小さくするとパネル内側の背景はほとんど歪まず平らに見え、値を大きくすると縁の背景が大きく押し込まれて厚いガラスのように見えます。
色分離幅の値を調整する 03 GLASS_CHROMA_SHIFT_PX 1 → 4 値を小さくすると縁の色はほとんど分かれずくっきりと見え、値を大きくすると赤・緑・青が目立つほどずれて重なって見えます。
弾性戻り時間の値を調整する 04 GLASS_ELASTIC_RETURN_MS 180 → 520 値を小さくすると離した瞬間ほとんど揺れずすぐ止まり、値を大きくすると元の形に戻るまで何度も長く揺れ続けます。

9枚のガラスパネルを面積・強度・動いたフレームで測る

以下はrun/421/demo以下の測定.jsonからそのまま取った数値で、見た目の印象ではなく実測値です。

項目 累積面積 強度 動いたフレーム
01 縁の屈折レンズ 23.1% 40.5 23/23
02 反射ハイライト追従 39.6% 57.7 20/23
03 色分離の縁 51.8% 92.3 23/23
04 弾性モーフドラッグ 31.8% 27.7 20/23
05 適応ティント輝度 74.7% 30.6 23/23
06 パネルの積み重ね奥行き 37.9% 30.8 23/23
07 リップルタッチプレス 26.0% 22.2 23/23
08 ボタン押し込みスクイーズ 14.8% 29.7 23/23
09 ヒーローナビバー場面 74.7% 45.4 23/23

05番と09番が上位に来るのは、その歪みやティントの計算がキャンバスのほぼ全域に及ぶ一方で、08番の押し込みはボタンの小さなドームだけにしか触れないためで、だからこそ08番の累積面積は14.8%と最も低くなっています。

どこで壊れるか — 動きを減らす設定・低スペック端末でのフレーム低下・背景なしで使う場合

九つのパネルはどれもgetImageData()・putImageData()で1フレームごとにピクセルバッファを読み書きしていて、その負荷はバッファ自体の面積に比例します。05番と09番がキャンバスのほぼ全体を毎フレーム計算しているのに対し、08番はボタンの小さなドームだけしか触れないので、低スペックの端末で真っ先にコマ落ちが出るのはおそらく05番か09番です。パスワードk28ehrdhは下のzip内にある九つのindex.html・style.scssをすべて開くための鍵になっていて、実際に自分の画像を背景に差し替えてこの負荷を確かめられます。単色でべったり塗った背景の上ではどの項目も意味を持ちません — 縁の歪み・色分離・ティントはどれも実際に隣接するピクセルを読み取っていて、同じ色しかない背景の上ではその読み取り結果が常に同じ色に戻ってしまい、ガラスらしさが消えてしまいます。

reduced-motion が止める演出と、07・08・09に残るキーボード操作

九つとも読み込み時に一度だけwindow.matchMedia('(prefers-reduced-motion: reduce)').matchesを確認していて、trueのときはパネルを隠すのではなく.is-demoクラスを外すので、種で動くデモは1枚の静止フレームのまま止まります。07番と08番はreduced-motionが有効でも操作できるままで、pointerdownに加えてEnter・Space用のkeydownハンドラーも付いているため、キーで押しても波紋や押し込みがそのまま起こります。09番も独自のkeydownハンドラーを持っていて、wheelイベントに加えてArrowUp・ArrowDown・PageUp・PageDownでも進み具合を動かせるので、マウスホイールに触れなくてもバーが厚くなる様子をキーボードだけで確認できます。02番と05番のハイライトやティントの変化はどれも元の色に足し算する形で効いていて、ガラスの上に乗る文字の可読性を損なわない設計です。

リキッドガラスエフェクトとグラスモーフィズムは何が違うか?

一般的なグラスモーフィズムのパネルはbackdrop-filter: blur()と半透明の塗りを重ねたレイヤーで、パネルのどこでも同じぼかしの値が均等にかかります。ここで扱ったリキッドガラス デザインのパネルは、各点の周りの実際のピクセルをgetImageData()で読み取って計算した量だけずらすので、押した場所は厚く、縁は鋭く、明るい部分と暗い部分でティントも変わるというように、パネルの表面ごとに歪み方が変わります。二つは同じページに同時に置けますが、ピクセルごとに何かを計算しているのはそのうち一つだけです。ガラスパネル自体の作り方だけを知りたいときは、CSS グラスモーフィズム効果9選【コピペOK】がぼかしと塗りの側だけを扱っています。

近くにある比較用のパターン

html5 canvas アニメーション9種 — 水滴とブラックホールは07番の波紋と同じようにピクセルバッファを読み書きしますが、パネルの縁だけでなく場面全体を歪ませます。レインボーグラデーション 9パターン — キャンバスで作るプラズマ背景も上の表と同じように一つの値だけを動かして確かめる作りですが、動かしているのは屈折ではなく色です。

この9つのリキッドガラスパターンをめぐるよくある疑問

この9つを作るのにWebGLやキャンバスライブラリは必要ですか?

いいえ。9つのパネルはすべて普通の<canvas>要素で、2Dコンテキスト自身のgetImageData()・putImageData()だけで読み書きしていて、WebGLの呼び出しも外部ライブラリも一つも使っていません。

リキッドグラス 作り方をコードで一から追うのと、このzipをそのまま使うのとではどちらがいいですか?

一から追うとboxEdgeSample()の計算そのものを理解できますが、zip内の九つのファイルはこの記事で測定した数値とまったく同じ状態なので、まず動いているものを確認してから読み解くほうが早く進められます。

08番の膨らみはなぜフェードアウトではなく縮むのですか?

squeezeがドームの高さ・サンプリング位置のずれ・縁の明るさと持ち上がりをまとめて一つの値で動かしているため、値が下がるとハイライトが単に暗くなるのではなく、ガラスの厚み自体が指先の下で薄くなったように見えるからです。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。