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

ダウンロード 進捗バー 9選 — 値がパーセントで更新される9つの方式

このダウンロード 進捗バー特集では、9つのうち1つは複数のファイルが同時に受信される間、各ファイルの進捗値が一覧の中で並んで上がり、1つは検証が失敗するたびに再試行回数の値が増えて進捗値が最初に戻り、もう1つは完了した項目の位置値が一覧の決まった領域へ移っていきます。

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

9つのうち8つは本物のボタンへのclickで動き始め、項目04だけはpointerdown・pointermove・pointerupという実際のドラッグ操作で順序の値を直接動かします。どの値も運任せでは動かず、加重合計・移動平均・経過時間の積算・線形またはスプリングの補間・あらかじめ決めた合否の配列・指数減衰のいずれかで計算され、Math.random()は一度も使われません。ここにはfetch()もFileもBlobもnavigator.onLineも登場せず、「受信中」「解凍中」「破損」はすべてブラウザ内で再計算されるローカル変数なので、以下のどの記述も「サーバーが転送を確認している」「実際の接続を検知している」という意味では読めません。ダウンロード 進捗 表示を自分のプロジェクトのために調べているなら、以下の9つはどれも本物のバイト数ではなくただのJavaScript変数からパーセントを計算しているという点を先に押さえておくと、ダウンロード 進捗 確認の実装を安全にコピーできます。

9つのパターンの解剖 — どの値がいつ変わるか

3つの項目(01・03・09)は1つの経過値や累積値をそのまま塗りバーのscaleXに使いますが、それぞれのやり方は異なります——01は3つの独立したファイル別の値を、3つのパーセントを均等に平均するのではなくSIZESで重み付けした4つ目の合計値にまとめ、03は一時停止するたびにpausedElapsedへそれまでの経過値を貯め込んでおくので一時停止が値を0へ巻き戻すことは一度もなく、09は同じ塗りバーを2通りに描き分け——一方は毎ティックtransformを更新し、もう一方は数字だけを差し替えて一切アニメーションしません。2つの項目(02・08)は一度に多くの小さな値から1つの値を組み立てます——02の移動平均は直近N個の生サンプルを混ぜてノイズの多い線の隣にもう1本なめらかな線を描き、08は経過時間の値を直接読む代わりにスプリング方程式を毎フレーム積分します。項目05は8つの点座標を1つの進捗値に合わせて一斉にlerpし、不透明度や拡大率をトゥイーンするのではなく多角形そのものの形を作り変えます。項目06の再試行回数と項目04の順序値はどちらも小さな整数で周りの値を並べ替えたり0に戻したりしますが、06のカウントは指数バックオフの待機時間を生み出す一方、04の順序は位置がイーズしていく目標を決めるだけです。項目07は9つの中で唯一、1サイクルに1回変わる値ではなく、決まった回数だけ繰り返すパスにまたがる指数減衰を計算しています。これらはどれも1つのダウンロード完了フラグが一度だけ切り替わるようなものではなく、9つ全てがデモの動いている間ずっと自分の値を一定周期で再計算し続け、9つのうち項目04だけが時間ではなく本物のドラッグからその値を更新します。

パターン別コード

01複数ファイル進捗値の並列更新 — 複数のファイルが同時に受信される間、各ファイルの進捗値が一覧の中でそれぞれ更新される方式

3つのファイルが同時に受信されている間、各行の塗りバーはそのファイル自身のSIZESの値に対する割合までスケールし、下の.dmf__row--total行は4つ目の値——3ファイル合計の受信バイト数をSIZESの合計で割った値——を描きます。これは3つのパーセントを均等平均したものではなく、容量で重み付けした合計です。一時停止すると全ての行のタイマーが同時に止まり、裏で進み続けることはありません。

複数ファイル進捗値の並列更新 — 複数のファイルが同時に受信される間、各ファイルの進捗値が一覧の中でそれぞれ更新される方式
新しいタブで開く

01 · 複数ファイル進捗値の並列更新 — 複数のファイルが同時に受信される間、各ファイルの進捗値が一覧の中でそれぞれ更新される方式 — 計測 累積面積 3.927% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dmf__row--totalSIZES.dmf__fill
  // シード固定: ファイルごとのサイズ(KB、仮想)とティックごとに加える速度配列(KB/ティック)。実fetch・File API 0
  var SIZES = [420, 980, 640];
  var SPEEDS = [
    [32, 40, 48, 52, 46, 38, 30, 44, 56, 60, 50, 36],
    [58, 64, 60, 52, 44, 38, 46, 54, 62, 58, 48, 40],
    [20, 26, 32, 38, 44, 40, 34, 28, 24, 30, 36, 42]
  ];
  var TICK_MS = 140;
  var HOLD_MS = 700;

  var done = [0, 0, 0];
  var tick = 0;
  var paused = false;
  var timer = null;

  // 計算層: 全体の進捗値 = 各ファイルの受信バイト合計 ÷ 全体サイズ合計(容量による加重平均、単純な3分の1平均ではない)
  function overall() {
    var d = done[0] + done[1] + done[2];
    var s = SIZES[0] + SIZES[1] + SIZES[2];
    return d / s;
  }

数値が示すこと

23フレームの計測で、項目01はフレーム面積の3.93%・強度スコア133.8・変化フレーム16という、3本の細い塗りバーと合計バー1本にとどまる値で、画面いっぱいの場面ではありません。

02転送速度値のグラフ更新 — 毎秒の転送量の値がグラフの形で描き直され続ける方式

160msごとに固定されたSAMPLES配列から新しい生の値が1つ届き、それと同時に直近N個の平均というもう1つの値が計算されるので、SVGは2本の折れ線——ノイズの多い.dsg__rawと、その隣になめらかな平均線——を描き直します。上部のラベルが表示するのは常にこの平均値で、生の値そのものではありません。

転送速度値のグラフ更新 — 毎秒の転送量の値がグラフの形で描き直され続ける方式
新しいタブで開く

02 · 転送速度値のグラフ更新 — 毎秒の転送量の値がグラフの形で描き直され続ける方式 — 計測 累積面積 8.108% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dsg__graphN.dsg__btn
  // 計算層: 直近N個の生サンプルの移動平均 — グラフの線はこの平均値をつなぐ(生の値そのものではない)
  function movingAvg(arr) {
    var out = [];
    for (var k = 0; k < arr.length; k++) {
      var s = Math.max(0, k - N + 1);
      var sum = 0, c = 0;
      for (var j = s; j <= k; j++) { sum += arr[j]; c++; }
      out.push(sum / c);
    }
    return out;
  }

数値が示すこと

23フレームの計測で、項目02はフレーム面積の8.11%・強度スコア108.0・変化フレーム16で、描き直され続けるSVGの折れ線がその面積の大半を占めています。

03一時停止・再開の状態値切替 — 状態値が一時停止と進行の間を切り替わるたびに、値の変化が止まりまた続く方式

経過時間の値はrequestAnimationFrameの中で数え続けますが、一時停止しても0には戻りません——pausedElapsedがそれまでの累積値を貯めておくので、再開すると止まったちょうどその値から続きます。このデモはTOTAL_MSの28%と62%で自動的に2回一時停止し、完了すると0に戻ってループします。

一時停止・再開の状態値切替 — 状態値が一時停止と進行の間を切り替わるたびに、値の変化が止まりまた続く方式
新しいタブで開く

03 · 一時停止・再開の状態値切替 — 状態値が一時停止と進行の間を切り替わるたびに、値の変化が止まりまた続く方式 — 計測 累積面積 1.799% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dpr__stateAUTO_PAUSE_AT.dpr__fill
  // 計算層: 経過時間 = 一時停止前の累積値(pausedElapsed) + (現在 − 再開時刻) — 再開しても0に途切れない
  function elapsed() {
    if (paused) return pausedElapsed;
    return pausedElapsed + (performance.now() - startedAt);
  }

  function paint() {
    var e = Math.min(TOTAL_MS, elapsed());
    var p = e / TOTAL_MS;
    fill.style.transform = 'scaleX(' + p.toFixed(4) + ')';
    pctEl.textContent = Math.round(p * 100) + '%';
    state.textContent = paused ? '停止' : (p >= 1 ? '完了' : '進行中');
    root.classList.toggle('dpr--paused', paused && p < 1);
    return p;
  }

数値が示すこと

23フレームの計測で、項目03はフレーム面積のわずか1.80%・強度スコア151.8・変化フレーム18という、9つの中で最も小さい形——1本のバーと短い状態文言だけです。

04待機列の順序値の並び替え — まだ開始していない項目の順序値がユーザーが決めた順番に変わる方式

4つの.dqr__item行は共有のorder配列を持ち、毎フレーム目標の行位置まで一気に飛ぶのではなく残り距離のLERP(0.18)分だけ近づきます。台本化されたautoSeqが900msごとに2つの待機項目を自動で入れ替える一方、本物のpointerdown・pointermove・pointerupの並びによって来場者が行を実際にドラッグして順序を変えることもできます。

待機列の順序値の並び替え — まだ開始していない項目の順序値がユーザーが決めた順番に変わる方式
新しいタブで開く

04 · 待機列の順序値の並び替え — まだ開始していない項目の順序値がユーザーが決めた順番に変わる方式 — 計測 累積面積 20.906% · 動いたフレーム 14/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dqr__itemLERPROW_H
  // 計算層: 順序の値が変わっても目標位置へ即ジャンプせず、毎フレーム残り距離のLERP比率だけ近づく
  function tickLerp() {
    for (var i = 0; i < items.length; i++) {
      if (i === dragging) continue;
      var t = targetY(i);
      y[i] += (t - y[i]) * LERP;
      items[i].style.transform = 'translateY(' + y[i].toFixed(2) + 'px)';
    }
    requestAnimationFrame(tickLerp);
  }
  requestAnimationFrame(tickLerp);

数値が示すこと

23フレームの計測で、項目04はフレーム面積の20.91%・強度スコア144.1・変化フレーム14という9つの中で最大の値です——4行が一斉に並び替わることは、単一のバーより遥かに広い面積を動かします。

05解凍進捗値と形状値の同時変化 — 解凍の進捗値が満ちていく間、形状の値も一緒に変わる方式

解凍の進捗値がrequestAnimationFrameのループの中で0から1へ満ちていく間、同じ進捗値がlerpPoints()を動かし、8つのBOXの頂点座標をそれぞれ対応するOPENの座標へ線形補間します。多角形は点ごとに形を変えていくのであって、2つの形を重ねてフェードしているのではありません。

解凍進捗値と形状値の同時変化 — 解凍の進捗値が満ちていく間、形状の値も一緒に変わる方式
新しいタブで開く

05 · 解凍進捗値と形状値の同時変化 — 解凍の進捗値が満ちていく間、形状の値も一緒に変わる方式 — 計測 累積面積 3.347% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dem__polyDUR_MS.dem__shape
  // 計算層: 進捗値(0〜1)で8つの点座標をそれぞれ線形補間(lerp) — 2つの形をクロスフェードするのではない
  function lerpPoints(t) {
    var out = [];
    for (var i = 0; i < BOX.length; i++) {
      var x = BOX[i][0] + (OPEN[i][0] - BOX[i][0]) * t;
      var y = BOX[i][1] + (OPEN[i][1] - BOX[i][1]) * t;
      out.push(x.toFixed(1) + ',' + y.toFixed(1));
    }
    return out.join(' ');
  }

  function paint(t) {
    poly.setAttribute('points', lerpPoints(t));
    fill.style.transform = 'scaleX(' + t.toFixed(3) + ')';
    pctEl.textContent = Math.round(t * 100) + '%';
  }

数値が示すこと

23フレームの計測で、項目05はフレーム面積の3.35%・強度スコア105.4・23フレーム中23フレーム全てで変化しており、9つの中で唯一、計測した全フレームで値が変わり続けています。

06破損時の再試行回数値 — 検証に失敗するたびに再試行回数の値が1つ増え、進捗値が最初に戻る方式

5つの要素を持つVERIFY_SEED配列が各回の「合否」を決め、最初の2回は必ず失敗するので塗りが0に戻りretryが1つずつ増え、次の待機時間はBASE_BACKOFF_MSにBACKOFF_MULTを再試行回数でべき乗した値になります——固定の待ち時間ではなく指数バックオフです。

破損時の再試行回数値 — 検証に失敗するたびに再試行回数の値が1つ増え、進捗値が最初に戻る方式
新しいタブで開く

06 · 破損時の再試行回数値 — 検証に失敗するたびに再試行回数の値が1つ増え、進捗値が最初に戻る方式 — 計測 累積面積 2.188% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dcr__retryBACKOFF_MULTVERIFY_SEED
  // 計算層: 再試行の待機 = 基準値 × 倍率^再試行回数(指数バックオフ) — 失敗するたびに待機が長くなる
  function backoffMs() {
    return BASE_BACKOFF_MS * Math.pow(BACKOFF_MULT, Math.min(retry, 4));
  }

  function paint() {
    fill.style.transform = 'scaleX(' + (pct / MAX_PCT).toFixed(3) + ')';
    retryEl.textContent = '再試行 ' + retry;
  }

数値が示すこと

23フレームの計測で、項目06はフレーム面積の2.19%・強度スコア158.7・変化フレーム16という9つの中で最も鋭い強度です——塗りが0に戻り状態文言の色が切り替わることが、形自体は小さくても急激で高コントラストな変化として計測されています。

07完了値のひらめき表示 — ダウンロードが終わる瞬間に完了値が1に変わり、一定時間後に0へ戻って消える方式

サイクルが表示区間に入った瞬間、斜めにゆがんだ.dcf__sweepの光がSWEEP_COUNT回までバッジの上を走り、各回の不透明度はDECAYをその回数でべき乗した値になるので、1回目は明るく3回目にはほぼ消えています——線形の減衰ではなく指数の減衰です。

完了値のひらめき表示 — ダウンロードが終わる瞬間に完了値が1に変わり、一定時間後に0へ戻って消える方式
新しいタブで開く

07 · 完了値のひらめき表示 — ダウンロードが終わる瞬間に完了値が1に変わり、一定時間後に0へ戻って消える方式 — 計測 累積面積 5.561% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dcf__sweepDECAYSWEEP_COUNT
  // 計算層: 光沢sweepの強さ = 初期値 × DECAY^回数(指数減衰) — 回を追うごとに薄れて消える
  function sweepOpacity(passIndex) {
    return Math.pow(DECAY, passIndex);
  }

  function frame(now) {
    var e = (now - start) % CYCLE_MS;
    var on = e < VISIBLE_MS;
    root.classList.toggle('dcf--on', on);
    if (on) {
      var passLen = SWEEP_MS;
      var passIdx = Math.floor(e / passLen);
      var passT = (e % passLen) / passLen;
      if (passIdx < SWEEP_COUNT) {
        var x = -30 + passT * 160; // %
        sweep.style.transform = 'translateX(' + x.toFixed(1) + '%) skewX(-18deg)';
        sweep.style.opacity = sweepOpacity(passIdx).toFixed(3);
      } else {
        sweep.style.opacity = '0';
      }
    }
    raf = requestAnimationFrame(frame);
  }

数値が示すこと

23フレームの計測で、項目07はフレーム面積の5.56%・強度スコアわずか57.4・変化フレーム17という9つの中で最も柔らかい強度です——sweepは半透明の光であって、はっきりした境界の切り替えではありません。

08完了項目の位置値の移動 — ダウンロードが終わった項目の位置値が一覧の決まった領域へ移る方式

2枚の.dfr__itemカードはそれぞれ違う遅延で自分のリストを離れ、共有の.dfr__zoneSlotへ向かいますが、その道のり上の位置は質量-バネ方程式(ZETA 0.42、W 7.5)を毎フレーム積分して得たspring(u)の値から来ます——イーズするCSSトランジションではなく、わざとスロットを行き過ぎてから減衰して止まる値です。

完了項目の位置値の移動 — ダウンロードが終わった項目の位置値が一覧の決まった領域へ移る方式
新しいタブで開く

08 · 完了項目の位置値の移動 — ダウンロードが終わった項目の位置値が一覧の決まった領域へ移る方式 — 計測 累積面積 12.302% · 動いたフレーム 10/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.dfr__zoneSlotZETAW
  // 定数: スプリング減衰比(zeta)・角振動数(w)・項目ごとの開始遅延(ms)・移動所要時間(ms)
  var ZETA = 0.42;
  var W = 7.5;
  var START_DELAY = [0, 1100];
  var MOVE_MS = 850;
  var CYCLE_MS = 2900;

  // 計算層: 位置の進捗を質量-バネ方程式で毎フレーム計算する(目標を行き過ぎてから戻って止まる) — CSSトランジションではない
  function spring(u) {
    var wd = W * Math.sqrt(1 - ZETA * ZETA);
    var e = Math.exp(-ZETA * W * u);
    return 1 - e * (Math.cos(wd * u) + (ZETA * W / wd) * Math.sin(wd * u));
  }

数値が示すこと

23フレームの計測で、項目08はフレーム面積の12.30%・強度スコア137.8・変化フレームは23中10という9つの中で最も少ない変化フレーム数です——スプリングはサイクルが繰り返す前にしっかり静止するので、注目すべきはフレーム占有率ではなくオーバーシュートそのものです。

09モーション削減時の静的ダウンロード表示 — モーション削減設定でアニメーションの代わりに進捗値の文言だけが変わる方式

同じパーセントの値が2つの板をわざと違う方法で描きます——左は毎ティックscaleXのtransformを更新し、右の.drm__staticBoxは文字ノードの中の数字を差し替えるだけでtransformもレイアウトの変化も一切ありません。ボタンは.drm--force-reduceクラスを切り替えて左の板自身のトランジションを止め、OSレベルのモーション削減設定の代わりを務めます。

モーション削減時の静的ダウンロード表示 — モーション削減設定でアニメーションの代わりに進捗値の文言だけが変わる方式
新しいタブで開く

09 · モーション削減時の静的ダウンロード表示 — モーション削減設定でアニメーションの代わりに進捗値の文言だけが変わる方式 — 計測 累積面積 0.643% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

.drm__staticBoxSTEP.drm--force-reduce
  // 定数: 進行速度(%/ティック)・ティック間隔(ms)
  var STEP = 4;
  var TICK_MS = 130;
  var forcedReduce = false;

  var pct = 0;

  // 計算層: 同じ1つの進捗値を2つの板が別々に描く — 左は毎ティックtransformを更新し、右は文言だけを差し替える(レイアウト・トランジション0)
  function paint() {
    fillA.style.transform = 'scaleX(' + (pct / 100).toFixed(3) + ')';
    pctA.textContent = pct + '%';
    pctB.textContent = pct + '%';
  }

  function tick() {
    pct = (pct + STEP) % 101;
    paint();
    setTimeout(tick, TICK_MS);
  }

数値が示すこと

23フレームの計測で、項目09はフレーム面積のわずか0.64%・強度スコア131.3・変化フレーム18という9つの中で最小の面積で、フレーム占有率ではなく2つの板をクリックのたびに並べて比較するための最小限の値です。

値がパターンを変える基準 — 更新周期・再試行基準・色段階の基準

移動平均の窓を広げる。 項目02のNを5から10に動かすと、平均線は直近の急な変化に鋭く反応しなくなり、なめらかさと引き換えに、生サンプルの急変への反応が一拍遅れます。

指数バックオフの倍率を下げる。 項目06のBACKOFF_MULTを2から1.6に動かすと、倍率2では再試行のたびに待機時間が急激に膨らみ間の停止が目立ちますが、1.6に下げると待機は緩やかに伸びるだけなので再試行がより近い間隔で続きます。

スプリング減衰比を上げる。 項目08のZETAを0.42から0.65に動かすと、0.42ではカードが.dfr__zoneSlotを目に見えて行き過ぎてから戻ってきますが、0.65に上げるとそのオーバーシュートが小さくなり、1回目でより静かに、より目標に近い位置へ収まります。

これら9つのどれも単体では1バイトもサーバーと確認していません。アップロード プログレスバー ローダー9種 — 計算するUI(329)は1つのアップロード自身の進捗ローダーが単独で上がっていく計算軸を扱っており、ここの項目01のように複数のダウンロードが1つの重み付き合計を共有するものとは異なります。無限スクロールローディング UIパターン9選 — スピナーから仮想ウィンドウまで(349)は一覧の下端で次の一群が読み込まれる計算軸を扱っており、この9つのどれかが100%に達するのとは違う引き金です。引っ張って更新 スマホ 9選 — 引いた距離で変わる更新のかたち(353)は引いた距離が更新を駆動する計算軸を扱っており、ここでは項目03自身の経過値の積算に最も近い兄弟です。この9つの計算はどれも、下のzipでcb2aueyyを打ち込んだ瞬間に開きます。

どこで壊れるか — オフライン復帰の瞬間・複数タブ同時ダウンロード・モーション削減

オフライン復帰の瞬間

9つのどれもnavigator.onLineを呼ばず、本物のonlineイベントも聞いていません。それどころか、そもそもネットワーク接続が切れるという状況自体をどれもモデル化していません——項目06のVERIFY_SEED配列は接続が戻ったかどうかではなく決まった順序で合否を決めるので、ここでの破損ファイルの再試行は接続の再接続ではなく整合性チェックの失敗の代役です。このデモは再試行が成功または失敗した後に何が起きるかは見せられますが、本物のネットワークが戻る瞬間そのものは見せられません——見せるべき本物のネットワークがそもそも存在しないからです。

複数タブ同時ダウンロード

同じ項目を別のタブで開くと、まったく独立したコピーが動き出します——9つのどれもlocalStorageやBroadcastChannel、その他タブ間で見える経路を読み書きしないため、項目01の3ファイルや項目06の再試行ループを2つのタブで同時に動かしても、互いの存在にはまったく気づきません。複数のタブでキューを共有する本物のダウンロードマネージャーには、まさにその共有チャンネルが必要で、それはここでは意図的に外されている部品です。

モーション削減

9つ全てが、それぞれ自身のSCSSにある1つの@media (prefers-reduced-motion: reduce)ブロックからモーション削減時の挙動を読み取っており、9つのどれもスクリプトの中でwindow.matchMedia()によって分岐してはいません——つまり計算そのものはOSの設定によってゲートされることは一度もなく、変わるのはtransitionの値だけです。どのブロックもボタンや塗り要素からtransitionを取り除くだけです——項目01の.dmf__fill/.dmf__btn、項目06の.dcr__fill/.dcr__btn、項目09の.drm__fill/.drm__btnなど——なので、requestAnimationFrameのループであれsetTimeoutの連鎖であれ、各値を動かしているJavaScript自体は前とまったく同じように毎回再計算され続けます。項目09だけがこれが実際に何を残すかを見せるために作られています——自身の.drm--force-reduceクラスをオンにすると、左の板の塗りは毎ティック受け取り続けるtransformの更新の間でトランジションしなくなり、もともとトランジションを持たない右の板はいつもと変わらないまま見えます。

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

バリエーション 変わる値 触れない値
移動平均の窓を広げる N 5 → 10(項目02) ティックごとのSAMPLES配列そのもの
指数バックオフの倍率を下げる BACKOFF_MULT 2 → 1.6(項目06) VERIFY_SEEDの合否の並び
スプリング減衰比を上げる ZETA 0.42 → 0.65(項目08) 項目08自身のW(7.5)

9つのどれもマークアップのどこにもaria-*属性もroleもtabindexも追加していません——この状態変化をスクリーンリーダーに届けるには、このコードを組み込む側が自分でライブリージョンを足す必要があります。ネイティブな部分としては、項目01から09までがそれぞれ本物の<button type="button">から発火しますが、項目04だけは例外で、ボタンを介さず行自体が直接pointerdown・pointermove・pointerupに反応します。9つのどれもdisabled属性をどの時点でも設定していません。状態変化にバンドルされたアイコン画像を差し替えている項目もありません——ここでの見た目の変化はすべて、クラス・transform・scaleX・または文字ノードを直接書き換えているだけです。

FAQ

項目04だけがクリックではなくpointerdown・pointermove・pointerupを必要とするのはなぜですか

待機列の並び替えは一瞬の操作ではなく連続したジェスチャーだからです——項目04はドラッグ中に行がどこにあるかを追い続けてorderを途中で更新する必要があり、これは1回のclickイベントでは表現できません。残り8つは押すたびに状態を1回切り替えるだけなのでclickリスナーで十分です。

この9つのパーセントは本物のファイルサイズや通信速度から来ていますか

いいえ——項目01のSIZES配列から項目02のSAMPLES配列まで、ここにある数値はすべてスクリプトに書かれた固定値です。fetch()を呼ぶことも、本物のContent-Lengthヘッダーを読むことも、実際の帯域を測ることもしていないので、各デモは学ぶための計算として見るべきで、信頼できる実際の転送としては見ないでください。

「ダウンロード マネージャーとは」何を指し、この9つはそれを再現していますか

いいえ、この9つは部品だけです——それぞれが加重合計・移動平均・スプリング・指数バックオフのどれか1つの計算だけに絞られており、共有キューや設定パネル、ファイル種別のアイコンをまとめ持つ本物のダウンロードマネージャー全体を組み立ててはいません。9つを1つのインターフェースに組み込むかどうかは別の設計判断で、zipの中身はそれを完全にあなた次第にしています。

出典: MDN — requestAnimationFrame · MDN — prefers-reduced-motion · MDN — transition

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

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