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

ページ めくり アニメーション css 9パターン — 紙のカール

ページ めくり アニメーション css。ライブラリなしでも、この動きはこの先も変わらない — 1番はポインターが触れた位置から巻き上がり、7番は揺れの値の分だけ紙飛行機が傾き、9番はその二つを一つの机の場面にまとめて見せるだろう。

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

1つの角から机全体の場面まで — 9つの並び順

以下の9つは、ポインターや矢印キーに直接答える一点の操作から、いくつかの技法が一つの値を共有する場面へと並んでいます。1番と5番はドラッグやキー操作にそのまま答え、2番から4番までは固定の時間経過を丸ごとのページフリップに変え、6番から8番までは色・歪み・連鎖という別々の方法で紙を動かし、9番は1番・4番・7番の技法を一つの机の上にまとめて、一つの数値だけで三つを同時に動かします。

01ポインターで引っ張ると角から巻き上がるページカール

角の近くをドラッグするとdrag.x/drag.yが更新され、curlPage()がその差をCURL_RADIUS_PXという一つの半径に変えて毎フレーム帯を巻き上げます。foldTheta()は引っ張った距離をその同じ定数で割るので、同じドラッグでも半径が小さければきつい折り目に、大きければ緩やかな巻きに見えます。

ポインターで引っ張ると角から巻き上がるページカール
新しいタブで開く

01 · ポインターで引っ張ると角から巻き上がるページカール — 計測 累積面積 35.251% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

CURL_RADIUS_PXfoldThetacurlPage
  function curlPage() {
    var foldX = clamp(drag.x, PAGE_L, PAGE_R);
    var skew = (drag.y - PAGE_B) / (PAGE_B - PAGE_T);   // 上へ引っ張るほど負になる — カール軸の傾き
    var h = PAGE_B - PAGE_T;
    ctx.fillStyle = PAPER;
    ctx.fillRect(PAGE_L, PAGE_T, Math.max(0, foldX - PAGE_L), h);

    var maxDx = Math.max(1, PAGE_R - foldX);
    for (var x = foldX; x <= PAGE_R + STRIP_W; x += STRIP_W) {
      var dx = Math.min(x - foldX, maxDx);
      var theta = foldTheta(dx);
      if (theta > Math.PI + 0.15) continue;
      var sxp = foldX + Math.sin(Math.min(theta, Math.PI)) * CURL_RADIUS_PX;
      var front = Math.cos(theta) > 0;
      var lift = (dx / maxDx) * skew * 30;
      ctx.fillStyle = front ? shadeHex(PAPER, 0.86 + 0.14 * Math.cos(theta)) : shadeHex(PAPER_BACK, 0.5 + 0.3 * (-Math.cos(theta)));
      ctx.fillRect(sxp, PAGE_T + lift, STRIP_W + 1, h);
    }
    var depth = clamp(maxDx / CURL_RADIUS_PX, 0, 1.6);

測定値: 累積面積35.3%、平均強度90.2、23/23フレームが動きました。

02折り目に沿って影が計算されるページフリップ

flipAngle()は経過時間を0からπまでの三角波に変え、pageFlip()はその角度の余弦をそのままctx.scale()に渡すので、ページはFOLD_Xを軸に回転し、二枚の画像を単に切り替えているわけではありません。同じ余弦が折り目の脇の影の幅と濃さも同時に決めるので、途中で止めたフリップほど影が濃く見えます。

折り目に沿って影が計算されるページフリップ
新しいタブで開く

02 · 折り目に沿って影が計算されるページフリップ — 計測 累積面積 41.608% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

FOLD_XflipAnglepageFlip
  function flipAngle(tMs) {
    var p = (tMs % FLIP_MS) / FLIP_MS;                // 0→1、2秒で2.2周
    var w = p < 0.5 ? p * 2 : (1 - p) * 2;             // 三角波
    return w * Math.PI;
  }

  function pageFlip() {
    var w = BOOK_R - BOOK_L, h = BOOK_B - BOOK_T;
    ctx.fillStyle = BACK_PAGE;
    ctx.fillRect(BOOK_L, BOOK_T, w, h);
    drawLines(BOOK_L, BOOK_T, w, h, LINE_COLOR);

    var c = Math.cos(angle);
    var frontFacing = c >= 0;
    var scaleW = Math.max(0.001, Math.abs(c)) * w;
    ctx.save();
    ctx.translate(FOLD_X, 0);
    ctx.scale(c === 0 ? 0.001 : c, 1);
    ctx.fillStyle = frontFacing ? FRONT_PAGE : FRONT_PAGE_BACK;
    ctx.fillRect(0, BOOK_T, w, h);

測定値: 累積面積41.6%、平均強度73.4、20/23フレームが動きました。

03複数ページが順番にめくれるブックスタック

stackThickness()は残りページ数remainingに固定の2.2pxを掛けるので、stepFlip()が1ページめくるたびスタック下の影が目に見えて薄くなります。nextPage()は前のフリップが終わってから次を始め、remainingが0になれば数がリセットされ、めくるページが尽きることはありません。

複数ページが順番にめくれるブックスタック
新しいタブで開く

03 · 複数ページが順番にめくれるブックスタック — 計測 累積面積 45.378% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

STACK_TOTALstackThicknessnextPage
  function stackThickness(n) { return clamp(n, 0, STACK_TOTAL) * 2.2; }

  function startFlip() {
    if (flipping || remaining <= 0) return;
    flipping = true; flipT = 0;
  }
  function nextPage() {
    if (flipping || remaining <= 0) { remaining = STACK_TOTAL; return; }
    startFlip();
  }

  function stepFlip(dt) {
    if (!flipping) return;
    flipT += dt * 1000;
    var p = clamp(flipT / FLIP_MS, 0, 1);
    angle = p * Math.PI;
    if (p >= 1) {
      flipping = false; angle = 0;
      remaining -= 1;
      if (remaining <= 0) remaining = STACK_TOTAL;      // 全部めくったら最初に戻る
    }
  }

測定値: 累積面積45.4%、平均強度75.7、18/23フレームが動きました。

04本の綴じ目の曲率で暗くなる見開きシーン

gutterBend()は経過時間を余弦のイージングにかけてBEND_MAX_RADを上限とする一つのbendAngleにまとめ、drawPageSide()はその同じ角度を二度使います — 各帯を綴じ目へ曲げるためと、暗くするためです — なので曲率と影は決して別々に動きません。角度が小さいうちはほとんど平らなままで、BEND_MAX_RADに近づくと帯が目に見えて内側へ沈み、綴じ目のあたりはほぼ黒く見えます。

本の綴じ目の曲率で暗くなる見開きシーン
新しいタブで開く

04 · 本の綴じ目の曲率で暗くなる見開きシーン — 計測 累積面積 31.942% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

BEND_MAX_RADgutterBenddrawPageSide
  function gutterBend(tMs) {
    return BEND_MAX_RAD * (0.5 - 0.5 * Math.cos(2 * Math.PI * tMs / PERIOD_MS));
  }

  function drawLines(x, y, w, h) {
    ctx.strokeStyle = 'rgba(23,20,26,.22)';
    ctx.lineWidth = 1.2;
    for (var i = 0; i < 5; i += 1) {
      var ly = y + h * (0.16 + i * 0.17);
      ctx.beginPath(); ctx.moveTo(x, ly); ctx.lineTo(x + w, ly); ctx.stroke();
    }
  }

  function drawPageSide(sign) {
    var strip = 4, h = PAGE_B - PAGE_T;
    for (var dx = 0; dx <= HALF_W; dx += strip) {
      var t = dx / HALF_W;                             // 0=綴じ目・1=外側の端
      var curveTheta = (1 - t) * bendAngle;             // 綴じ目に近いほど曲率が大きい
      var comp = Math.sin(curveTheta) * 12;             // 綴じ目側へ押し込まれる幅(px)
      var x = CENTER + sign * (dx - comp);
      var shade = 1 - (1 - t) * (bendAngle / BEND_MAX_RAD) * 0.42;
      ctx.fillStyle = shadeHex(PAPER, shade);
      ctx.fillRect(sign > 0 ? x : x - strip, PAGE_T, strip + 1, h);
    }
  }

測定値: 累積面積31.9%、平均強度22.8、23/23フレームが動きました。

05折り線の角度で曲がる折り紙フォールド

angleDegは毎フレームtargetDegとの差の5分の1だけ近づき、drawFacet()はその一つの角度の余弦で各面の幅と明るさを同時に決めます — 0°に近いとほぼ平らに見え、CREASE_ANGLE_DEGの上限に近いと面が狭くなって目に見える折れたくさびになります。矢印キーはtargetDegを直接動かすので、既定の範囲を超えて同じ式を押し広げることもできます。

折り線の角度で曲がる折り紙フォールド
新しいタブで開く

05 · 折り線の角度で曲がる折り紙フォールド — 計測 累積面積 29.063% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

CREASE_ANGLE_DEGangleStepdrawFacet
  function drawFacet(sign) {
    var rad = angleDeg * Math.PI / 180;
    var c = Math.cos(rad);
    var frontFacing = c >= 0;
    var h = BOTTOM - TOP;
    ctx.save();
    ctx.translate(CENTER, 0);
    ctx.scale(sign * (c === 0 ? 0.001 : c), 1);
    var brightness = frontFacing ? (0.62 + 0.38 * c) : (0.3 + 0.2 * (-c));
    ctx.fillStyle = frontFacing ? shadeHex(PAPER, brightness) : shadeHex(PAPER_BACK, brightness);
    ctx.fillRect(0, TOP, FACET_W, h);
    if (frontFacing) {
      ctx.strokeStyle = 'rgba(23,20,26,.18)';
      ctx.lineWidth = 1;
      ctx.beginPath(); ctx.moveTo(FACET_W * 0.5, TOP + 8); ctx.lineTo(FACET_W * 0.5, BOTTOM - 8); ctx.stroke();
    }
    ctx.restore();
  }

測定値: 累積面積29.1%、平均強度104.3、23/23フレームが動きました。

06しわがノイズで陰影づけられる紙の質感

makeValueNoise()は24×24の格子をランダム値で一度だけ作り、呼び出すたびに隣接4点をsmoothStep()で混ぜるので、crumpleHeight()は格子の生の乱数点を飛び移るのではなく連続した凹凸の場として読み取ります。同じノイズ値が描かれた面を持ち上げるのと暗くするのを両方担っているので、高いしわほど隣に落ちる影も必ず深くなります。

しわがノイズで陰影づけられる紙の質感
新しいタブで開く

06 · しわがノイズで陰影づけられる紙の質感 — 計測 累積面積 50.638% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

makeValueNoisecrumpleHeightlatAt
  function makeValueNoise(seed) {
    var rnd = seedRand(seed);
    var GRID = 24;
    var lattice = [];
    for (var i = 0; i < GRID * GRID; i += 1) lattice.push(rnd());
    function latAt(xi, yi) {
      xi = ((xi % GRID) + GRID) % GRID; yi = ((yi % GRID) + GRID) % GRID;
      return lattice[yi * GRID + xi];
    }
    return function valueNoise(x, y) {
      var xi = Math.floor(x), yi = Math.floor(y);
      var xf = x - xi, yf = y - yi;
      var v00 = latAt(xi, yi), v10 = latAt(xi + 1, yi), v01 = latAt(xi, yi + 1), v11 = latAt(xi + 1, yi + 1);
      var sx = smoothStep(xf), sy = smoothStep(yf);
      var a = v00 + (v10 - v00) * sx;
      var b = v01 + (v11 - v01) * sx;
      return a + (b - a) * sy;
    };
  }

測定値: 累積面積50.6%、平均強度20.7、23/23フレームが動きました。

07放物線と揺れの値で飛ぶ紙飛行機

driftFlight()は発着点を結ぶ直線の補間にDRIFT_AMPLITUDE_PXでスケールした正弦波を足し込み、それとは別の放物線が同じ点をARC_HEIGHTの分だけ持ち上げてから下ろします。揺れの値を小さくすると横のぶれはほぼ消え、大きくすると機体は直線の経路をはるかに超えて振れ、空中を漂うページカールのようにも見えます。

放物線と揺れの値で飛ぶ紙飛行機
新しいタブで開く

07 · 放物線と揺れの値で飛ぶ紙飛行機 — 計測 累積面積 20.715% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

DRIFT_AMPLITUDE_PXdriftFlightdrawPlane
  function driftFlight(t) {
    var x = lerp(LAUNCH.x, LAND.x, t) + Math.sin(t * DRIFT_CYCLES * Math.PI * 2) * DRIFT_AMPLITUDE_PX;
    var y = lerp(LAUNCH.y, LAND.y, t) - ARC_HEIGHT * 4 * t * (1 - t);
    return { x: x, y: y };
  }

  var pos = driftFlight(0), heading = 0, curT = 0;

測定値: 累積面積20.7%、平均強度188.1、23/23フレームが動きました。

08関節の値がつながって揺れる紙人形

jointAngle()は各関節の正弦波をchainIndex * FK_DELAY_RADだけずらし、Math.pow(0.82, chainIndex)で振れ幅を縮めるので、fkChain()は肩から指先まで、減衰しながら遅れる角度を前の点に足しながら歩きます — すべての関節が揃って振れるのではありません。結果は連鎖反応のように見えます — 肩がまず動き、後に続く関節ほど一拍遅れて少しだけ小さく揺れます。

関節の値がつながって揺れる紙人形
新しいタブで開く

08 · 関節の値がつながって揺れる紙人形 — 計測 累積面積 17.033% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

FK_DELAY_RADjointAnglefkChain
  function jointAngle(t, def, chainIndex) {
    var w = Math.PI * 2 * t;
    var sway = Math.sin(w - chainIndex * FK_DELAY_RAD) * rad(SWING_AMP_DEG) * Math.pow(0.82, chainIndex);
    return def.base + sway;
  }

  function swayFactor(t) { return Math.sin(Math.PI * 2 * t); }

  function fkChain(originX, originY, segs, t) {
    var x = originX, y = originY, cum = 0;
    var pts = [{ x: x, y: y }];
    for (var i = 0; i < segs.length; i += 1) {
      cum += jointAngle(t, segs[i], i);
      x += Math.cos(cum) * segs[i].seg[0];
      y += Math.sin(cum) * segs[i].seg[0];
      pts.push({ x: x, y: y });
    }
    return pts;
  }

測定値: 累積面積17.0%、平均強度95.2、23/23フレームが動きました。

09上の技法を机の上にまとめたページめくりヒーロー

valueStep()が動かすcompositeValueという一つの値が、gutterBend()の見開きの曲率とcurlPage()の緩んだ一枚の半径、そして紙飛行機の発着フェーズを同時に動かします — 1番・4番・7番が三つの別々のタイマーではなく一つの数値を共有しているということです。その一つの値が上がっていく様子を見るだけで、三つの技法がそれぞれ机全体の構図にどれだけ効いているかがわかります。

上の技法を机の上にまとめたページめくりヒーロー
新しいタブで開く

09 · 上の技法を机の上にまとめたページめくりヒーロー — 計測 累積面積 17.499% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

CURL_RADIUS_MAX_PXcompositeValuedrawBook
  function curlPage(t) {
    var radius = 6 + t * CURL_RADIUS_MAX_PX;
    var stripCount = 14;
    for (var i = 0; i < stripCount; i += 1) {
      var u = i / stripCount;
      var localX = u * CURL_W;
      var theta = Math.max(0, localX - (CURL_W - radius * 2.4)) / Math.max(1, radius);
      theta = clamp(theta, 0, Math.PI);
      var sx = CURL_CX + CURL_W - CURL_W * (1 - u) - Math.min(localX, CURL_W - radius * 2.4);
      var screenX = CURL_CX + (CURL_W - radius * 2.4) * u * 0 + localX - (1 - Math.cos(theta)) * radius;
      var front = Math.cos(theta) >= 0;
      var bright = front ? (0.6 + 0.4 * Math.cos(theta)) : (0.32 + 0.18 * (-Math.cos(theta)));
      ctx.fillStyle = front ? shadeHex(PAPER, bright) : shadeHex(PAPER_BACK, bright);
      var stripX = CURL_CX + localX - (localX > (CURL_W - radius * 2.4) ? (1 - Math.cos(theta)) * radius : 0);
      ctx.fillRect(stripX, CURL_TOP, CURL_W / stripCount + 1, CURL_BOT - CURL_TOP);
    }
  }

測定値: 累積面積17.5%、平均強度123.2、23/23フレームが動きました。

実際にどこで使われるか

1番の角ドラッグと5番の折り紙は、読者自身のドラッグやキー操作が本当に紙を曲げているように感じさせたい電子書籍リーダーや紹介ページのイントロオブジェクトに向いています。2番から4番はギャラリーの次へボタン・カタログやルックブックのビューアー・レシピやマニュアルの見開き画面にちょうど合い、どれもカーソルが偶然通っただけでなく読者がクリックしたときに自動再生されます。6番のノイズしわはメモや付箋カードの背景として控えめに使え、7番の紙飛行機と8番の紙人形はどちらも機能ボタンというより空状態やローディング画面のイラストとして読めます。そして9番の机の場面はランディングページのヒーロー向けに作られていて、3つの技法が一つの値で動く様子をまとめて見せることに意味があります。

値が紙の形を変える場所 — カール半径・折り線角度・揺れ幅

項目 累積面積 平均強度 動いたフレーム
01 角ドラッグカール 35.3% 90.2 23/23
02 折り目シャドウフリップ 41.6% 73.4 20/23
03 複数ページスタックフリップ 45.4% 75.7 18/23
04 綴じ目ベンドシェード 31.9% 22.8 23/23
05 折り紙クリースフォールド 29.1% 104.3 23/23
06 ノイズシェードクランプル 50.6% 20.7 23/23
07 ドリフトアーク紙飛行機 20.7% 188.1 23/23
08 FK関節チェーン紙人形 17.0% 95.2 23/23
09 机ヒーローコンポジット 17.5% 123.2 23/23

06番の紙の質感は9つの中で累積面積が最も広く(50.6%)、平均強度は最も低い部類(20.7)です — 鋭く跳ねるのではなく、広く緩やかに陰影が動くからです。07番の紙飛行機はその逆で、面積は控えめ(20.7%)なのに平均強度は9つで最も高く(188.1)、小さな機体がフレームを横切るたびピクセル数は少なくても変化そのものは大きいからです。この差の大半は3つの定数が作っています — CURL_RADIUS_PXとCURL_RADIUS_MAX_PX(1番・9番)は巻き上がる縁の幅を、CREASE_ANGLE_DEG(5番)は面全体の幅をどれだけ深く折るかを、DRIFT_AMPLITUDE_PX(7番)は直線の経路からどれだけ外れるかを決めていて、結局はどれも同じ種類の数値をキャンバスの別々の場所で読んでいるだけです。

どこで壊れるか — モーション低減設定・低スペック端末・レイアウトシフト

9つすべてがrequestAnimationFrameのループにだけ.is-demoを使っていて、1番・5番・9番はポインターのpointerdown/pointermoveや矢印キーにも実際に答え、3番はEnter・スペース・ArrowRightでも次のページをめくります — この入力処理はループの有無に関係なく動きます。prefers-reduced-motion: reduceはis-demoを外し、各パターンを開始フレームではなく代表的な一状態に固定します — 4番の綴じ目はBEND_MAX_RADの60%で止まり、3番はすでに3ページめくった状態で止まり、8番は4分の1周期の途中で止まるので、モーションを減らした読者にも平らなページではなく折れたページが見えます。レイアウトシフトは別に注意する点です — どのキャンバスも最初の描画前に480×300の固定枠でfit()済みですが、fit()が一度走った後にブラウザ幅をすばやく変えると、次のresizeイベントがfit()をもう一度呼ぶまで2番から4番の折り目や1番・9番のカールの中心はずれたままになります。9つすべてのindex.html・style.scss一式を開く3zppfsppはこの段落に置いてあります — ここまで読んだ方なら、どの定数から動かすかもう決まっているはずです。

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

バリエーション 値の変化 見え方の変化
ページカールの半径値を調整する CURL_RADIUS_PX 60 → 160 値を小さくすると角はごく狭く巻かれ少しだけ折れたように見え、値を大きくすると円筒に近い丸みに見えます
折り線の角度値を調整する CREASE_ANGLE_DEG 30 → 120 値を小さくするとほぼ平らなままに見え、値を大きくすると深く曲がり完全に折れた立体に見えます
紙飛行機の揺れ値を調整する DRIFT_AMPLITUDE_PX 8 → 30 値を小さくするとほぼ直線に近く飛び、値を大きくすると左右に大きく揺れながら飛びます

3つのどれも上で説明したprefers-reduced-motionの挙動は変えません — CURL_RADIUS_PXが60でも160でも、モーション低減時は同じ代表フレームに固定されたままです。それでもアクセシビリティ側にまだ必要なものがあります — 9つのキャンバスはどれもスクリーンリーダーが読み上げるテキストを持たないので、実際に使うときはラッパーに平易なaltかaria-labelを添える必要があります。

ページめくりアニメーションとはこのページカールと何が違うか?

紙 めくる アニメーションと聞くと、折り紙のように一枚の紙が複数の折り線で新しい形へ畳まれていく動き全般を指すことが多く、5番の折り紙フォールドはまさにその例です。今回の検索語にある「ページ めくり アニメーション」はもっと狭い意味で、一枚のシートの片端だけを曲げる動きを指し、ポインターで駆動するとき本 ページめくり アニメーションと呼ばれることもありますが、成功の基準は新しい形に畳むことではなく、紙そのものらしく見えること — 縁の少したわみ、カールが深まるほど濃くなる影です。9番の机の場面はその両方を並べて見せます — 本の綴じ目は4番と同じように曲がり、緩んだ一枚は1番と同じようにカールし、そのどちらもフレームを毎回描き直しているだけで、二枚の画像を切り替えているわけではありません。

ページ めくり アニメーション を組み込む前によくある質問

無料のページめくりライブラリでこの9つの代わりになりますか?

既製のライブラリは2番・3番・4番の代わりになります — どれも固定のタイマーやクリックで二枚の画像を切り替えるだけの動きだからです。ですが1番・5番・6番・7番・8番・9番の代わりにはなりません — これらはライブラリが持つ二状態のクロスフェードではなく、ドラッグ量・折り線角度・ノイズ場・揺れの位相といった生きた値をフレームごとに読んでピクセル位置を描き直しているからです。ページ めくり アニメーション js の実例を探すときも、この違いを最初に確かめる価値があります。

9番はなぜ3つの技法を1つの値だけで動かしているのですか?

valueStep()が動かすのはcompositeValueという1つの数値だけで、gutterBend()・curlPage()・紙飛行機の発着フェーズはすべてその同じ数値を読み返しているからです。3つのタイマーを手で同期させる代わりに、1つの値を3つの描画関数がそれぞれ違う角度から見ているだけなので、机の構図全体は実質compositeValueそのものです。

毎フレームのカール描画はCSSだけのフリップより重いですか?

はい、ループが回すストライプ数に比例します — 1番と9番は毎フレーム14本から24本の縦ストライプをそれぞれMath.sin/Math.cosのペアで描き直しますが、2番の土台にあるようなCSSのrotateY()フリップはコンポジタに平らな矩形をフレームごとに一度描き直させるだけです。480×300の固定枠は9つとも変わらないのでレイアウトは動かず、増える負荷はペイントだけにとどまります。

同じく「1つの値を毎フレーム読む」という考え方でまとめた編としては、カード フリップ アニメーション 9選【コピペ20行】が2番から4番までと同じく固定軸のまわりに平らな矩形を回していて近く、スクロールテリング 作り方 9選 — コピペOKのCSSは駆動する値がドラッグやタイマーではなくスクロール量である点で似た発想です。そして物理エンジン 作り方 9選 — Verletロープからラグドールまでは8番の連鎖する関節と見比べる価値があります — どちらも1点ではなく複数の連結点から動きを作っているからです。

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

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