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

画像 切り替え エフェクト 9種 — キャンバスが計算するリビール

画像 切り替え エフェクト 9種のうち、1つはピクセルの対角座標から通過時刻を計算して斜めに掃き、1つは格子マスごとにノイズしきい値を先に計算して不規則だが再現される順で現れ、1つは固定シードで横列オフセットを計算し毎回同じ順でグリッチが揃います。

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

1枚の画像を別の画像へ切り替える方法が、この9種にまとまっています。対角座標が斜めの境界を掃くもの、角度が境界を時計回りの円形リビールに変えるもの、値ノイズフィールドが不規則だが再現される順でマスを溶かすもの、溶解順が四角ブロックを一つずつ現すもの、中心からの距離順が三角の破片を落とすもの、固定シードが横列をずらしてから揃え直すもの、複数のインク起点からの距離フィールドが縁のにじんだ形で滲み広がるもの、マスごとの明暗値が網点の半径を同時に育てるもの、そしてサイン波の位相が水面のような境界を押し上げるものです。どれもcss 画像 切り替え アニメーションの重いライブラリには頼らず、1枚の小さなキャンバスパネルとクリック・キー操作の土台の上に、名前の付いた計算関数がひとつ乗っているだけです。

デモ

対角座標で計算される画面切り替えワイプ
新しいタブで開く

01 · 対角座標で計算される画面切り替えワイプ — 計測 累積面積 48.76% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

tFieldoutImgputImageData

上のパターン01はtFieldという配列と、それをgetImageData/putImageDataで読み書きしながらピクセルの対角位置をリビールの通過時刻へ変換するRENDER関数だけで動いています。残り8つも同じ形で、それぞれ専用のコードブロックを添えて下に並びます。

9つの画像切り替えエフェクトの解剖 — どこで計算の一層が回るか

# パターン 累積面積 平均強度 動いたフレーム
01 対角座標ワイプ 48.76% 126.4 23 / 23
02 角度掃引の円形リビール 48.249% 132.6 23 / 23
03 ノイズしきい値ディゾルブ 48.761% 169.8 20 / 23
04 溶解順モザイクリビール 48.051% 147.4 22 / 23
05 距離順の破片落下 48.444% 83.5 23 / 23
06 固定シードの横列グリッチ 47.115% 158.8 23 / 23
07 インク距離フィールドのにじみ 48.568% 115.0 23 / 23
08 明暗値束縛の網点成長 34.422% 79.9 21 / 23
09 サイン位相の波リビール 47.283% 163.5 22 / 23

9つともrequestAnimationFrameを直接呼んでいて、CSSのtransitionや@keyframesがピクセル計算を肩代わりしている箇所はありません。それぞれ1つのFloat32Array(座標フィールド・マスごとのしきい値・破片の順序のいずれか)を一度だけ準備し、あとは毎フレーム同じ配列をRENDER(p)が読み返してgetImageData/putImageDataで書き戻すか、drawImage/clip/fillRectで描き直します。

パターン03は累積面積が最も広く(48.761%)、平均強度も最も高い(169.8)反面、動いたフレーム数は9つ中最少(20/23)です。ノイズしきい値のマスがソフトな1ピクセル単位のフェードではなく16px単位の塊としてまとめて切り替わるため、切り替わったフレームは一度に大きく変化する一方、しきい値を跨がない何フレームかは何も動きません。逆にパターン08は面積が最小(34.422%)で強度も最も低く(79.9)、ドットの半径がそのマス自身の明暗値で頭打ちになるため、暗いマスは進行率が1に達しても大きなドットに育たず、画面の一部がそのまま埋まらずに残るからです。css 画像 切り替え フェードとして実写の写真を使う場面では、明暗の分布が今回の合成シーンより不均一になりやすく、この差はさらに開くと考えられます。

一度だけ準備し毎フレーム読み返す座標フィールド

パターン01・02・07は、キャンバス全体のピクセル数分のFloat32ArrayをPRECOMPUTEで一度だけ組み立て、RENDERのピクセルループで同じフィールドを読み返します。01のtFieldは各ピクセルの対角位置(x/iw + y/ih)を半分にした値を保持します。02のangFieldはMath.atan2でパネル中心からの角度を求め、12時方向がゼロになるよう回転させたものです。07のdistFieldは複数の固定インク起点からの距離を、縁がにじんで見えるよう値ノイズのオフセットとブレンドしています。

固定シードや明暗値でマスごと・列ごとに決まる値

パターン03・06・08はピクセル単位ではなくマスまたは列ごとに1つの値を先に計算しておき、RENDERはこの3つに関してはピクセルデータに一切触れず、進行率と保存済みの値を比べてdrawImage・fillRect・arcを呼ぶだけです。03のcellNoiseは固定の順列テーブル(buildPerm)から作った値ノイズ関数で、各マスに不規則だが再現されるしきい値を割り当てます。06のrowOffsetとrowOrderは同じ固定シード生成器(makeLcg)から生まれるため、同じ列は毎回同じ横幅だけずれます。08のdotRは各マスの平均明暗値を一度読み取り、そのマスのドット半径の上限にします。

進行率から毎フレーム計算し直す幾何情報

パターン04・05・09は準備段階を軽く済ませ、代わりにRENDER自身の中で小さな幾何計算をやり直します。04は行と列からmeltOrderという順位をブロックごとに先に計算しておき、まだ溶けきっていないブロックの色付き矩形が毎フレームどれだけ縮んで下の鮮明な画像を透かすかを計算し直します。05は各三角破片の中心からの距離順を先に計算しておき、落下中の破片が毎フレームどれだけ下へ移動し、どれだけ透明になるかを計算し直します。09は準備段階をほぼ省き、列の位置と現在の進行率からサイン関数で境界の高さを毎フレーム一から求め直します。

パターン別コード

計算関数は9つ、1パターンにつき1つです。どれもindex.htmlから連続した範囲をそのまま切り出したもので、簡略化した書き直しではありません。

01対角座標で計算される画面切り替えワイプ

パネル全体を覆うキャンバスが各ピクセルの対角座標をtFieldから読み戻し、毎フレームそのピクセルがまだ古い画像を見せているべきか新しい画像を見せているべきかを判定します。結果として現れる境界は、一方の角からもう一方の辺へ向かって進む一本の斜め線です。

対角座標で計算される画面切り替えワイプ
新しいタブで開く

01 · 対角座標で計算される画面切り替えワイプ — 計測 累積面積 48.76% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

tFieldoutImgputImageData
  function RENDER(p) {
    var o = outImg.data;
    for (var i = 0, n = tField.length; i < n; i++) {
      var t = tField[i], k = i * 4, reveal;
      if (t <= p) reveal = 1;
      else if (t <= p + EDGE_SOFT) reveal = 1 - (t - p) / EDGE_SOFT;
      else reveal = 0;
      o[k] = dataB[k] + (dataA[k] - dataB[k]) * reveal;
      o[k + 1] = dataB[k + 1] + (dataA[k + 1] - dataB[k + 1]) * reveal;
      o[k + 2] = dataB[k + 2] + (dataA[k + 2] - dataB[k + 2]) * reveal;
      o[k + 3] = 255;
    }
    ctx.putImageData(outImg, 0, 0);
  }

02角度で計算される時計回りの円形リビール

01と同じピクセルブレンドのループがここでも回りますが、読み取るフィールドが対角位置ではなくパネル中心からの角度になっているため、境界は横に滑るのではなく時計の針のように回転します。現在の掃引角度に細いアクセント線が重ねて描かれるので、動きの途中でも進行方向がひと目でわかります。

角度で計算される時計回りの円形リビール
新しいタブで開く

02 · 角度で計算される時計回りの円形リビール — 計測 累積面積 48.249% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

angFieldstrokeStyleputImageData
  function RENDER(p) {
    var o = outImg.data;
    for (var i = 0, n = angField.length; i < n; i++) {
      var t = angField[i], k = i * 4, reveal;
      if (t <= p) reveal = 1;
      else if (t <= p + EDGE_SOFT) reveal = 1 - (t - p) / EDGE_SOFT;
      else reveal = 0;
      o[k] = dataB[k] + (dataA[k] - dataB[k]) * reveal;
      o[k + 1] = dataB[k + 1] + (dataA[k + 1] - dataB[k + 1]) * reveal;
      o[k + 2] = dataB[k + 2] + (dataA[k + 2] - dataB[k + 2]) * reveal;
      o[k + 3] = 255;
    }
    ctx.putImageData(outImg, 0, 0);
    ctx.save();
    ctx.strokeStyle = ACCENT; ctx.lineWidth = 2; ctx.globalAlpha = .8;
    var ang = p * Math.PI * 2 - Math.PI / 2;
    var r = Math.min(W, H) * 0.42;
    ctx.beginPath();
    ctx.moveTo(cx0 / dpr, cy0 / dpr);
    ctx.lineTo(cx0 / dpr + Math.cos(ang) * r, cy0 / dpr + Math.sin(ang) * r);
    ctx.stroke();
    ctx.restore();
  }

03値ノイズフィールドでマスごとの登場順を計算するディゾルブ

パネルは小さなマスの格子に分けられ、各マス自身の値ノイズの読み取り値が、そのマスが古い画像から新しい画像へ切り替わる瞬間を決めます。ノイズフィールドは固定シードから生成されるため、見た目は不規則でも同じマスは常に同じ順番で溶けていきます。

値ノイズフィールドでマスごとの登場順を計算するディゾルブ
新しいタブで開く

03 · 値ノイズフィールドでマスごとの登場順を計算するディゾルブ — 計測 累積面積 48.761% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

buildPermcellNoisefadeN
  function PRECOMPUTE() {
    cols = Math.ceil(W / NOISE_CELL_PX);
    rows = Math.ceil(H / NOISE_CELL_PX);
    cellNoise = new Float32Array(cols * rows);
    for (var r = 0; r < rows; r++) {
      for (var c = 0; c < cols; c++) cellNoise[r * cols + c] = noise2(c * NOISE_FREQ, r * NOISE_FREQ);
    }
  }

  function RENDER(p) {
    ctx.drawImage(bufB, 0, 0, W, H);
    var cw = Math.round(NOISE_CELL_PX * dpr), ch = Math.round(NOISE_CELL_PX * dpr);
    for (var r = 0; r < rows; r++) {
      for (var c = 0; c < cols; c++) {
        if (cellNoise[r * cols + c] <= p) {
          var sx = Math.round(c * NOISE_CELL_PX * dpr), sy = Math.round(r * NOISE_CELL_PX * dpr);
          ctx.drawImage(bufA, sx, sy, cw, ch, c * NOISE_CELL_PX, r * NOISE_CELL_PX, NOISE_CELL_PX, NOISE_CELL_PX);
        }
      }
    }
  }

04ブロックサイズが時間から計算されて縮むモザイク解像

粗い格子の各ブロックは最初、平均化された単色の平面を見せています。行と列から計算されたmeltOrderという値が、そのブロックが下の鮮明な画像に切り替わる瞬間を決めます。切り替わる直前には、その単色矩形の高さが縮んでいき、鮮明な画像が下から溶け上がるように見えます。

ブロックサイズが時間から計算されて縮むモザイク解像
新しいタブで開く

04 · ブロックサイズが時間から計算されて縮むモザイク解像 — 計測 累積面積 48.051% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

avgColormeltOrderdrawImage
  function RENDER(p) {
    ctx.drawImage(bufB, 0, 0, W, H);
    for (var r = 0; r < rows; r++) {
      for (var c = 0; c < cols; c++) {
        var order = meltOrder[r * cols + c];
        var bx = c * BLOCK_PX, by = r * BLOCK_PX;
        if (order > p) {
          var sx = Math.round(c * BLOCK_PX * dpr), sy = Math.round(r * BLOCK_PX * dpr);
          ctx.drawImage(bufA, sx, sy, Math.round(BLOCK_PX * dpr), Math.round(BLOCK_PX * dpr), bx, by, BLOCK_PX, BLOCK_PX);
        } else if (order > p - MELT_WINDOW) {
          var u = (p - order) / MELT_WINDOW;
          var h = BLOCK_PX * (1 - u);
          ctx.fillStyle = avgColor[r * cols + c];
          ctx.fillRect(bx, by + BLOCK_PX * u * 0.6, BLOCK_PX, h);
        }
      }
    }
  }

05三角片ごとの落下を計算する破片トランジション

画面は小さな三角の破片に分割されていて、各破片の中心からの距離が、その破片が落ち始める瞬間を決めます。落下が始まった破片は自分の三角形にクリップされ、しきい値を過ぎた進行率の分だけ真下へ平行移動しながら、同時に透明度も下がっていきます。

三角片ごとの落下を計算する破片トランジション
新しいタブで開く

05 · 三角片ごとの落下を計算する破片トランジション — 計測 累積面積 48.444% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

clipTriglobalAlphamaxDist
  function RENDER(p) {
    ctx.drawImage(bufB, 0, 0, W, H);
    for (var i = 0; i < shards.length; i++) {
      var s = shards[i];
      if (s.dist > p) {
        ctx.save(); clipTri(s.tri);
        ctx.drawImage(bufA, 0, 0, cv.width, cv.height, 0, 0, W, H);
        ctx.restore();
      } else {
        var u = Math.min(1, (p - s.dist) / FALL_WINDOW);
        if (u < 1) {
          var off = u * u * FALL_DIST_PX;
          ctx.save();
          ctx.globalAlpha = 1 - u;
          ctx.translate(0, off);
          clipTri(s.tri);
          ctx.drawImage(bufA, 0, 0, cv.width, cv.height, 0, -off, W, H);
          ctx.restore();
        }
      }
    }
  }

06横列オフセットを固定シードで計算するグリッチトランジション

パネルは細い横列に分けられ、固定シードの生成器がレンダリング開始前に各列の左右オフセットを一度だけ割り当てます。進行率がその列自身の積み順を過ぎるにつれ、そのオフセットはゼロへ向かって縮み、列は最終的に整列した位置へ落ち着いて新しい画像を見せます。

横列オフセットを固定シードで計算するグリッチトランジション
新しいタブで開く

06 · 横列オフセットを固定シードで計算するグリッチトランジション — 計測 累積面積 47.115% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

makeLcgrowOffsetrowOrder
  function PRECOMPUTE() {
    rowsN = Math.ceil(H / ROW_PX);
    var rng = makeLcg(SEED_GLITCH); // 固定シード — フレームごとに引き直さない
    rowOffset = new Float32Array(rowsN);
    rowOrder = new Float32Array(rowsN);
    for (var i = 0; i < rowsN; i++) {
      rowOffset[i] = (rng() * 2 - 1) * GLITCH_MAX_PX;
      rowOrder[i] = i / rowsN;
    }
  }

  function RENDER(p) {
    for (var i = 0; i < rowsN; i++) {
      var order = rowOrder[i];
      var src = order <= p ? bufA : bufB;
      var near = Math.abs(order - p);
      var dx = near < GLITCH_WINDOW ? rowOffset[i] * (1 - near / GLITCH_WINDOW) : 0;
      var dy = i * ROW_PX;
      var sh = Math.min(ROW_PX, H - dy);
      if (sh <= 0) continue;
      var sy = Math.round(dy * dpr), shPx = Math.round(sh * dpr);
      ctx.drawImage(src, 0, sy, cv.width, shPx, dx, dy, W, sh);
    }
  }

07距離フィールドで滲み半径を計算するインクにじみリビール

いくつかの固定点をインクの起点に見立て、各ピクセルから最も近い起点までの距離を、値ノイズでわずかにブレンドしてから読み取ります。その値を進行率が超えた瞬間からピクセルが切り替わるため、結果は均一な掃引ではなく、少数の起点からインクがにじみ広がっていくように見えます。

距離フィールドで滲み半径を計算するインクにじみリビール
新しいタブで開く

07 · 距離フィールドで滲み半径を計算するインクにじみリビール — 計測 累積面積 48.568% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

distFieldnoiseIbuildPerm
  function RENDER(p) {
    var o = outImg.data;
    for (var i = 0, n = distField.length; i < n; i++) {
      var t = distField[i], k = i * 4, reveal;
      if (t <= p) reveal = 1;
      else if (t <= p + EDGE_SOFT) reveal = 1 - (t - p) / EDGE_SOFT;
      else reveal = 0;
      o[k] = dataB[k] + (dataA[k] - dataB[k]) * reveal;
      o[k + 1] = dataB[k + 1] + (dataA[k + 1] - dataB[k + 1]) * reveal;
      o[k + 2] = dataB[k + 2] + (dataA[k + 2] - dataB[k + 2]) * reveal;
      o[k + 3] = 255;
    }
    ctx.putImageData(outImg, 0, 0);
  }

08明暗値で半径を計算する網点リビール

画像は格子状のドットへサンプリングされ、各ドット自身の平均明暗値がそのドットの育つ上限を決めます。進行率が進むにつれて全てのドットが自分の上限へ向かって一斉に半径を広げていくため、印刷の網点のように画面全体が少しずつ埋まっていきます。

明暗値で半径を計算する網点リビール
新しいタブで開く

08 · 明暗値で半径を計算する網点リビール — 計測 累積面積 34.422% · 動いたフレーム 21/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

dotsXdotRdotColor
  function RENDER(p) {
    ctx.drawImage(bufB, 0, 0, W, H);
    for (var i = 0; i < dotsX.length; i++) {
      var r = dotR[i] * Math.min(1, p * 1.15);
      if (r < 0.6) continue;
      ctx.save();
      ctx.beginPath(); ctx.arc(dotsX[i], dotsY[i], r, 0, Math.PI * 2); ctx.clip();
      ctx.fillStyle = dotColor[i];
      ctx.fillRect(dotsX[i] - DOT_PX, dotsY[i] - DOT_PX, DOT_PX * 2, DOT_PX * 2);
      ctx.restore();
    }
  }

09サイン波の位相差で境界を計算する波リビール

1本の境界線が画面を下から上へ掃き、その列ごとの高さがサイン波によってずらされることで、線がまっすぐではなく波打つように見えます。ある列の境界より上はまだ古い画像にクリップされ、下は新しい画像を見せています。

サイン波の位相差で境界を計算する波リビール
新しいタブで開く

09 · サイン波の位相差で境界を計算する波リビール — 計測 累積面積 47.283% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

baseYbeginPathdrawImage
  function RENDER(p) {
    ctx.drawImage(bufB, 0, 0, W, H);
    var baseY = H * (1 - p);
    var amp = WAVE_AMPLITUDE_PX * Math.sin(p * Math.PI);
    for (var x = 0; x < W; x += COL_STEP) {
      var y = baseY + Math.sin(x * WAVE_FREQ + p * WAVE_PHASE_SPEED) * amp;
      if (y >= H) continue;
      y = Math.max(0, y);
      ctx.save();
      ctx.beginPath(); ctx.rect(x, y, COL_STEP, H - y + 1); ctx.clip();
      ctx.drawImage(bufA, 0, 0, W, H);
      ctx.restore();
    }
  }

値が画面を変える場所 — 対角座標・ノイズしきい値・固定シード

01 — EDGE_SOFT

この定数は、対角の境界線に沿って「まだ古い画像」から「完全に新しい画像」までの間に置かれる帯の幅を決めるものです。境界の角度そのものではなく、その一本の縁がどれだけ柔らかく、あるいは硬く見えるかだけを変えます。

03 — NOISE_CELL_PX

これは1つのディゾルブマスのピクセルサイズです。ノイズの値自体ではなく、しきい値を越えたときに一緒に切り替わる塊がどれだけ粗いか、細かいかだけを変えます。

09 — WAVE_AMPLITUDE_PX

これはサイン波が境界の基準線からどれだけ高く押し上げられるかの最大値です。波の速さではなく、境界が上へ進むときにどれだけ大きく波打つ幅だけを変えます。

バリエーション 変わる値 見た目の変化
EDGE_SOFT 0.05 → 0.015 対角境界の柔らかさ幅が0.05から0.015へ下がる 古い画像と新しい画像の間の帯が急に狭まり、対角の境界がソフトなグラデーションではなくほぼ硬い切れ目として見える
NOISE_CELL_PX 16px → 8px ディゾルブマスのサイズが16pxから8pxへ縮む 一度に現れる塊の面積が4分の1になり、ディゾルブが少数の目立つブロックではなく細かく散った粒として広がる
WAVE_AMPLITUDE_PX 10px → 26px 波の振幅が10pxから26pxへ上がる 境界の波打ちが2倍以上高くなり、水面のような曲線が上へ進むにつれてより荒く、はっきり見える

どこで壊れるか — アクセシビリティ・reduced-motion・低スペック

9つとも同じ土台を共有しています。クリック可能なパネルにはrole="button"・tabindex="0"・再生し直すと何が起きるかを説明するaria-labelが付き、キャンバス自体はaria-hidden="true"でラベルと重複しないようにしてあり、どのパネルもclickとEnter・Spaceのkeydownの両方に応答します。window.matchMedia('(prefers-reduced-motion: reduce)')はCSSのメディアクエリだけに任せず、読み込み時に一度JavaScript側でも読み取られます。

reduced-motionが有効なとき、9つとも自動でループするdriveTickの呼び出しをまるごとスキップし、is-stillクラスを付け、RENDER(0.5)へ直接ジャンプして半分ずつ見える状態をすぐ表示し、クリック・Enterのハンドラーを、アニメーションさせずに進行率を0と1の間で一気に切り替えるものへ差し替えます。この設定の下でいつも通りのイージングを裏で動かし続けるパターンは1つもありません。

9つの計算関数を1つずつ読むのではなくまとめて見比べるなら、パスワードzd738fjgで開くzipそのものを読むのが一番早い方法です。

getImageData/putImageDataで全ピクセルに触れる3パターン(01・02・07)は、端末のピクセル比でスケールされたキャンバス全体サイズの配列を毎回RENDERで再計算するため、9つの中では低スペックな端末で最も重くなります。残り6つはマス・破片・ドット・列のいずれか一部だけをdrawImage/fillRect/arcで触るだけなので、実際のピクセルコピーはブラウザ自身のコンポジターが担い、フレームあたりのコストははるかに軽くなります。

この全ピクセル走査のコストは、実際のページに実際の端末ピクセル比で組み込んで初めて表面化します。下のzipに入っている9つのファイルは、まさにその条件で確かめるためのものです。

3つのバリエーションとアクセシビリティ

上の表がすでに、確認済みの3つのバリエーション — EDGE_SOFT・NOISE_CELL_PX・WAVE_AMPLITUDE_PX — をすべて含んでいます。どのバリエーションもその1つの定数だけを書き換えたもので、リスナー・aria-*属性・reduced-motionの分岐は元のパターンとまったく同じままです。バリエーションのindex.htmlは、その1つの数値だけを変えた元ファイルなので、上で説明したアクセシビリティの挙動をバリエーションごとに確認し直す必要はありません。

画像 切り替え エフェクト の実装例はどこで見られる?

画像 トランジションの実装例として多く見つかるのは、単純なCSSのopacityクロスフェードか、1方向だけをclip-pathアニメーションに固定したワイプです。上の9つのパターンはそれとは違う場所に立っていて、座標フィールド・マスごとのしきい値・破片ごとの順序のいずれかを素のJavaScriptで一度だけ計算し、毎フレーム読み戻してキャンバスに描かせています — チャートやアニメーションのライブラリもCDNのスクリプトも使わず、上のzipとブラウザだけで完結します。ギャラリーの画像切り替え、ヒーローバナーの写真差し替え、読み込み画面のビフォーアフター表示のいずれも、同じクリック・キー操作の土台をそのまま流用しつつ、9つの計算関数のうち雰囲気に合う1つを差し込むだけで作れます。

FAQ

パターン03のノイズディゾルブは、なぜ9つの中で平均強度が最も高いのですか?

各マスは1ピクセルずつフェードするのではなく、drawImageで16px単位の塊としてまとめて切り替わります。複数のマスが同時にしきい値を越えるフレームでは、一度に広い面積が変化するため、01・02・07の緩やかな1ピクセル単位のブレンドより1フレームあたりの差分が大きくなります。

パターン07のインクにじみの縁は、1つの距離点から計算されていますか、それとも複数ですか?

複数です。PRECOMPUTEが固定数のインク起点を配置し、各ピクセルについて最も近い起点までの距離だけを残したうえで、値ノイズのオフセットをブレンドします。そのため縁は完全な円ではなく、にじんだ不規則な形として現れます。

このcss 画像 切り替え アニメーション自動デモの構成は、実際のギャラリーにそのまま使えますか?

click・keydownのリスナー、aria-labelとrole="button"の組み合わせ、そして各計算関数はそのまま移せます。デモの2枚の固定シーンを、実際のギャラリーで必要な2枚の画像に差し替えるだけで、タイミングの計算式に手を入れる必要はありません。どのパターンも意図的に省いているのは2枚目の画像の先読みだけなので、公開する前にそこだけは自分で足してください。

Sources: MDN — getImageData · MDN — putImageData · MDN — requestAnimationFrame · MDN — prefers-reduced-motion · MDN — CanvasRenderingContext2D.clip

関連記事: html5 canvas アニメーション9種 — 水滴とブラックホール · CSS 画面切り替え アニメーション9選 · パーリンノイズ地形9選 — ライブラリなし、計算だけで — 1つ目は同じピクセル計算の骨組みをリビールではなく1つの場面全体に使っており、2つ目はキャンバスのピクセル処理を使わないCSSだけの画面切り替えを扱い、3つ目はパターン03がマスごとのしきい値に使っているのと同じ値ノイズの計算をさらに深く掘り下げています。

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

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