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

価格表 デザイン9選 — 年払い切替・割引率まで計算して見せる

価格表 デザインは、料金プランのボタン一つ・数字一つに計算の層を重ねる考え方です。年払い・月払いの切替が変わるたびに各プランの価格を計算し直し、割引バッジは定価と割引価格の差を自分で求め、法人窓口セクションは利用量の値からその場で見積もりを計算する、価格表ページ用セクション9種です。

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

デモ

9項目とも「見た目の演出ではなく、割引率・利用量・レビュー評価・画面幅というデータを計算し直して表示価格・強調度・バッジ文言・座標に反映する」という同じ前提を共有しています。実測値は次のとおりです。

番号 項目 累積面積 強度平均 動いたフレーム
01 プランカード 16.26% 103.0 18/23
02 比較表 29.96% 39.6 7/23
03 課金トグル 5.26% 99.6 10/23
04 割引バッジ 1.45% 97.3 23/23
05 料金FAQ 34.94% 81.3 9/23
06 レビュー 19.76% 106.7 7/23
07 法人窓口 2.29% 121.6 8/23
08 パートナーバー 8.32% 194.6 9/23
09 料金フッター 14.08% 89.1 7/23

05料金FAQが累積面積34.94%で最も広いのは、開いたパネルの実際の高さがカード全体を押し広げるためです。04割引バッジが1.45%・07法人窓口が2.29%と狭いのは、変わる領域がそれぞれバッジの帯・スライダーのつまみだけに限られる構造上の値で、この2項目の狭さは欠陥ではありません。動いたフレームは02比較表・06レビュー・09料金フッターが7フレームで最も少なく、これはシナリオが2000ms間隔で切り替わる構造上の待機区間です。反対に04割引バッジは23フレームすべてで動くカウントダウンのため毎フレーム再計算が走り、08パートナーバーは強度平均194.6で最も明るく、複数のロゴが一度に表示・非表示を切り替えます。9項目とも下限を満たしています。

価格表ページの解剖 — 9つのセクションが計算するもの

「価格表 デザイン テンプレート」で検索して出てくるものの多くは、色と余白を整えただけの静的な型です。ここで扱う9つのセクションは、その手前に計算の層を1枚重ねます。年払い・月払いの切替、定価と割引価格の差、入力した利用量、レビュー評価というデータを毎回読み直し、表示価格・強調度・バッジ文言・座標に変換し直す設計です。

何が入力で何が出力か

入力は月額・割引率・定価と割引価格・利用量・レビュー評価・画面幅の6種類です。出力はCSS変数か、DOMに直接書き込むテキストのどちらかで、レイアウトのキーフレーム自体は変えません。

なぜ一枚の層に計算をまとめるのか

割引率の適用先を各所に散らすと、定価を変えるたびに複数箇所を直す必要が出ます。yearlyMonthlyEqやfindMatchのような一つの関数に計算をまとめておくと、データが変わってもその関数を呼び直すだけで表示全体が揃います。

セクション別コード

価格表 デザインを探して来た人は、まずこの9項目の実装から見ると早いです。自動再生は各項目内で固定したシナリオの配列をsetIntervalで順番に切り替えるだけで、Math.random()は一度も使いません。

01プランカード — 年払い・月払いの切替に合わせて価格を求め直すカード列

PLANS配列は月額12・29・59に対して割引率.15・.20・.25をそれぞれ持たせてあり、切替が切り替わるたびにyearlyMonthlyEqがmonthly掛ける(1引く discount)を求め直します。求めた新しい価格へはanimateValueがrequestAnimationFrameでイージング1引く(1引くp)の3乗を通しながら古い数字から1フレームずつ描き直すので、12から10・29から23・59から44という値へ数字が転がるように変わります。

プランカード — 年払い・月払いの切替に合わせて価格を求め直すカード列
新しいタブで開く

01 · プランカード — 年払い・月払いの切替に合わせて価格を求め直すカード列 — 計測 累積面積 16.259% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

requestAnimationFramearia-checkedtranslateX

ROLL_MSの600msをrequestAnimationFrameで割り切る

ROLL_MSは600ms固定の定数で、経過時間をこの値で割ったpが1に達した瞬間にstepのループが自ら止まります。

var PLANS = [
  { monthly: 12, discount: .15 },
  { monthly: 29, discount: .20 },
  { monthly: 59, discount: .25 }
];
var ROLL_MS = 600;

function yearlyMonthlyEq(plan) {
  return Math.round(plan.monthly * (1 - plan.discount));
}

function animateValue(el, from, to, duration) {
  if (!el) return;
  if (reduce || from === to) { el.textContent = String(to); return; }
  var start = null;
  function step(ts) {
    if (start === null) start = ts;
    var p = Math.min((ts - start) / duration, 1);
    var eased = 1 - Math.pow(1 - p, 3);
    el.textContent = String(Math.round(from + (to - from) * eased));
    if (p < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

02比較表 — プラン間で値が違う行だけを数えて強調する表

applyは行ごとにallEqualで全プランの値が同じかどうかを調べ、違う行だけdiffCountを1つずつ増やしながらis-diffクラスを付けます。強調の濃さはその場限りの固定値ではなく、--diff-alphaというCSS変数に.12足す(diffCountを行数で割ってから.35を掛けた値)という式の結果を毎回書き込むことで決まります。

比較表 — プラン間で値が違う行だけを数えて強調する表
新しいタブで開く

02 · 比較表 — プラン間で値が違う行だけを数えて強調する表 — 計測 累積面積 29.956% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

--diff-alphaquerySelectorAllscope="row"

diffCountの比率がCSS変数--diff-alphaに変わる

差がある行の割合が高いほど--diff-alphaが.12から.47の間で大きくなり、chipには「差がある行○/○」という実数がそのまま表示されます。

function allEqual(arr) {
  return arr.every(function (v) { return v === arr[0]; });
}

function apply(scenario) {
  var diffCount = 0;
  ROW_KEYS.forEach(function (key) {
    var values = scenario[key];
    var tr = rowsEl.querySelector('tr[data-row="' + key + '"]');
    if (!tr) return;
    var cells = tr.querySelectorAll('.v');
    cells.forEach(function (cell, i) { cell.textContent = values[i]; });
    var differs = !allEqual(values);
    if (differs) diffCount += 1;
    tr.classList.toggle('is-diff', differs);
    tr.style.setProperty('--diff-alpha', differs ? String(.12 + (diffCount / ROW_KEYS.length) * .35) : '0');
  });
  chip.textContent = '差がある行 ' + diffCount + '/' + ROW_KEYS.length;
}

03課金トグル — スイッチの位置から節約額を求めてバッジに載せる切り替え

DISCOUNTは.20固定で、スイッチが年払い側になるとapplyがmonthlyに12とDISCOUNTを掛けた値を求めてバッジ文言に入れます。月額29なら年間70・月額49なら年間118という節約額がその場で計算され、月払いに戻すとbadge.hiddenが真になって帯ごと消えます。

課金トグル — スイッチの位置から節約額を求めてバッジに載せる切り替え
新しいタブで開く

03 · 課金トグル — スイッチの位置から節約額を求めてバッジに載せる切り替え — 計測 累積面積 5.264% · 動いたフレーム 10/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

aria-checkedrole="switch":not([hidden])

STEPSの4通りをtoggleのaria-checkedに映す

STEPS配列が持つ4通りの状態を順に切り替えるたびに、toggleのaria-checkedとmodeMonthly・modeYearlyのis-activeが同時に付け替わります。

var DISCOUNT = .20;
var STEPS = [
  { monthly: 29, yearly: false },
  { monthly: 29, yearly: true },
  { monthly: 49, yearly: false },
  { monthly: 49, yearly: true }
];

function apply(step) {
  priceNum.textContent = String(step.monthly);
  toggle.setAttribute('aria-checked', step.yearly ? 'true' : 'false');
  modeMonthly.classList.toggle('is-active', !step.yearly);
  modeYearly.classList.toggle('is-active', step.yearly);
  if (step.yearly) {
    var savings = Math.round(step.monthly * 12 * DISCOUNT);
    badge.hidden = false;
    badge.textContent = '年間 ' + savings + ' お得';
  } else {
    badge.hidden = true;
  }
}

04割引バッジ — 定価と割引価格の差からパーセントを自分で求めるバッジ

定価LIST 49と割引価格SALE 32の差を定価で割ったpctは35(%)で、この計算結果だけがバッジ文言になります。LOOP_MS 2000msごとにTOTAL_SEC 720秒(12分)のカウントダウンが一巡し、残り時間の割合がURGENT_RATIO .25を下回った瞬間にバッジとカウントダウンへis-urgentクラスが付きます。

割引バッジ — 定価と割引価格の差からパーセントを自分で求めるバッジ
新しいタブで開く

04 · 割引バッジ — 定価と割引価格の差からパーセントを自分で求めるバッジ — 計測 累積面積 1.447% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

requestAnimationFrameMath.flooraria-live

formatとframeが720秒を分秒表示に変える

formatがTOTAL_SECを分と秒に割り直し、frameはprogressが1に達するまでrequestAnimationFrameのループを続けます。

var LIST = 49;
var SALE = 32;
var pct = Math.round((LIST - SALE) / LIST * 100);
pctBadge.textContent = pct + '%OFF';

var LOOP_MS = 2000;
var TOTAL_SEC = 12 * 60;
var URGENT_RATIO = .25;

function format(sec) {
  var m = Math.floor(sec / 60);
  var s = Math.floor(sec % 60);
  return m + ':' + (s < 10 ? '0' + s : String(s));
}

function frame(elapsed) {
  var progress = Math.min(elapsed / LOOP_MS, 1);
  var remainSec = TOTAL_SEC * (1 - progress);
  countdown.textContent = format(remainSec) + ' 残り';
  var urgent = (remainSec / TOTAL_SEC) < URGENT_RATIO;
  pctBadge.classList.toggle('is-urgent', urgent);
  countdown.classList.toggle('is-urgent', urgent);
  if (progress >= 1) return LOOP_MS;
  return null;
}

05料金FAQ — 開いている項目数に合わせて一覧の高さを測り直すアコーディオン

開閉のたびにgrid-template-rowsを一時的にautoへ戻してscrollHeightで実際の高さを測り、測り終えたら0pxに戻して次の遷移をそこから始めます。開いている項目だけの高さをNATURAL_HEIGHTSから合計し直すので、複数開いても一覧全体の高さが実測値のまま正しく伸びます。

料金FAQ — 開いている項目数に合わせて一覧の高さを測り直すアコーディオン
新しいタブで開く

05 · 料金FAQ — 開いている項目数に合わせて一覧の高さを測り直すアコーディオン — 計測 累積面積 34.936% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

grid-template-rowsscrollHeightaria-expanded

offsetHeightを読むだけでスタイルが反映される理由

panel.offsetHeightを読むだけの一行はブラウザに強制的にレイアウトを計算させる合図で、これがないと0pxへの変更が次の遷移の開始値として反映されません。

var NATURAL_HEIGHTS = items.map(function (item) {
  var panel = item.querySelector('.a');
  if (!panel) return 0;
  // grid-template-rows を一時的に auto に戻し、遷移・クラスに関係なくコンテンツの実際の高さを測る。
  var prevTransition = panel.style.transition;
  panel.style.transition = 'none';
  panel.style.gridTemplateRows = 'auto';
  var h = panel.scrollHeight;
  panel.style.gridTemplateRows = '0px';
  void panel.offsetHeight; // 0px を実際に反映(フラッシュ)しないと、次の遷移がこの値から始まらない
  panel.style.transition = prevTransition;
  return h;
});

function measureAndReport() {
  var total = 0;
  items.forEach(function (item, i) {
    if (item.classList.contains('is-open')) total += NATURAL_HEIGHTS[i];
  });
  chip.textContent = 'リストの高さ ' + total + 'px';
}

06料金レビュー — 評価の平均を自分で求めて示すレビュー集

averageはreduceで評価を合計してから件数で割るだけの単純な計算ですが、レビューの組み合わせを差し替えるたびにこの値を求め直すので平均は常に今表示している集合と一致します。レビュー文は-webkit-line-clampで3行(340px以下の狭い画面では2行)までに切り詰められ、そのうえでカードの高さはrequestAnimationFrameの中で全カードのscrollHeightを比べ、その最大値をminHeightとして全カードに書き戻すことで揃えます。

料金レビュー — 評価の平均を自分で求めて示すレビュー集
新しいタブで開く

06 · 料金レビュー — 評価の平均を自分で求めて示すレビュー集 — 計測 累積面積 19.763% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

requestAnimationFramescrollHeightline-clamp

maxHをrequestAnimationFrameの中で測る理由

テキストを差し替えた直後は高さがまだ反映されていないため、1フレーム待ってからscrollHeightを測ることで正しい最大値がとれます。

function average(reviews) {
  var sum = reviews.reduce(function (acc, r) { return acc + r.rating; }, 0);
  return sum / reviews.length;
}

function apply(set) {
  avgNum.textContent = average(set.reviews).toFixed(1);
  avgCount.textContent = String(set.count);
  set.reviews.forEach(function (r, i) {
    quotes[i].textContent = '“' + r.text + '”';
    whos[i].textContent = r.who + ' · ' + r.rating + '★';
  });
  requestAnimationFrame(function () {
    var maxH = 0;
    cards.forEach(function (card) { maxH = Math.max(maxH, card.scrollHeight); });
    cards.forEach(function (card) { card.style.minHeight = maxH + 'px'; });
    chip.textContent = 'カードの高さ ' + maxH + 'px';
  });
}

07法人窓口CTA — 入力した利用量からその場で見積もりを求めるセクション

利用量STEPSは50・300・800・1200の4段階で切り替わり、THRESHOLD 1000以下ならBASE 19000にusage掛けるRATE(単価18)を足した見積もりを表示します。800なら19000足す800掛ける18は33400という見積もりが出ますが、1200のようにTHRESHOLDを超えると計算をやめて「営業チームにお問い合わせください」という案内文言に切り替わります。

法人窓口CTA — 入力した利用量からその場で見積もりを求めるセクション
新しいタブで開く

07 · 法人窓口CTA — 入力した利用量からその場で見積もりを求めるセクション — 計測 累積面積 2.287% · 動いたフレーム 8/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

aria-valuenowclientWidthrole="slider"

usageの割合がスライダーのknobLeftになる

usageをMAX_USAGEで割った比率にtrackWidthからknobWidthを引いた長さを掛けることで、knobのleftとfillの幅が同じ比率で動きます。

var MAX_USAGE = 1500;
var BASE = 19000;
var RATE = 18;
var THRESHOLD = 1000;
var STEPS = [50, 300, 800, 1200];

function apply(usage) {
  var pct = usage / MAX_USAGE;
  var trackWidth = track.clientWidth;
  var knobWidth = knob.offsetWidth || 16;
  var knobLeft = pct * (trackWidth - knobWidth);
  knob.style.left = knobLeft + 'px';
  fill.style.width = (pct * 100) + '%';
  knob.setAttribute('aria-valuenow', String(usage));
  usageNum.textContent = String(usage);

  if (usage > THRESHOLD) {
    estimate.textContent = '営業チームにお問い合わせください';
    estimate.classList.add('is-contact');
  } else {
    var won = BASE + usage * RATE;
    estimate.textContent = '予想料金 ' + won.toLocaleString('ko-KR') + 'ウォン';
    estimate.classList.remove('is-contact');
  }
}

08パートナーロゴバー — 画面幅に合わせて表示数を求め直す列

WIDTHS配列の220・320・420という3通りの画面幅が切り替わるたびに、GAP 8pxを挟んだロゴ1つ分の実測幅からfitCountをMath.floorで求め直します。求めた数を超えるロゴはdisplay:noneで隠れ、画面が広がれば同じ式がより大きなfitCountを返してロゴが増えます。

パートナーロゴバー — 画面幅に合わせて表示数を求め直す列
新しいタブで開く

08 · パートナーロゴバー — 画面幅に合わせて表示数を求め直す列 — 計測 累積面積 8.32% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

querySelectorAlloffsetWidthtext-overflow

itemWidthの実測値からfitCountの上限を決める

Math.min(fitCount, items.length)という一行があるおかげで、幅が十分広くてもロゴの実数を超えて表示されることはありません。

var GAP = 8;
var WIDTHS = [220, 320, 420];

function apply(width) {
  viewport.style.width = width + 'px';
  var itemWidth = items[0].offsetWidth || 80;
  var fitCount = Math.max(1, Math.floor((width + GAP) / (itemWidth + GAP)));
  fitCount = Math.min(fitCount, items.length);
  items.forEach(function (item, i) {
    item.style.display = i < fitCount ? '' : 'none';
  });
  chip.textContent = fitCount + '個表示中';
}

09料金フッター — 入力した条件から合うプランを自分で求めて強調するおすすめ

LIMITSはBasic(座席1・容量5)・Pro(座席10・容量50)・Team(座席30・容量500)の3段階を持ち、findMatchはNEEDSの各条件を上から順に比べて座席数と容量の両方を満たす最初のプランを返します。条件が座席3・容量20ならBasicでは座席が足りずProが選ばれ、どのプランも満たせない場合はLIMITSの最後のプランを既定として返します。

料金フッター — 入力した条件から合うプランを自分で求めて強調するおすすめ
新しいタブで開く

09 · 料金フッター — 入力した条件から合うプランを自分で求めて強調するおすすめ — 計測 累積面積 14.082% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

querySelectorAllgrid-template-columnsscale(1.05)

headcountとstorageの両方を満たす最初の行を探す

forループはseatsとstorageGbの両方が条件以上になった時点で即座にそのプラン名を返すため、片方だけ条件を満たすプランは選ばれません。

var NEEDS = [
  { headcount: 3, storage: 20 },
  { headcount: 1, storage: 3 },
  { headcount: 15, storage: 80 },
  { headcount: 30, storage: 400 }
];

function findMatch(need) {
  for (var i = 0; i < LIMITS.length; i++) {
    if (LIMITS[i].seats >= need.headcount && LIMITS[i].storageGb >= need.storage) return LIMITS[i].name;
  }
  return LIMITS[LIMITS.length - 1].name;
}

function apply(need) {
  headcountEl.textContent = String(need.headcount);
  storageEl.textContent = String(need.storage);
  var match = findMatch(need);
  planEls.forEach(function (el) {
    el.classList.toggle('is-match', el.getAttribute('data-name') === match);
  });
  chip.textContent = 'おすすめプラン ' + match;
}

どこで使うページか — 料金表ページを作り直すチーム

サブスクリプションの料金表ページを作り直すチームがまず詰まるのは、01のように年払い・月払いの切替ボタンを押した瞬間、すべての価格を求め直す計算です。月額と年額を別々に決め打ちしておくと、プランが1つ増えるたびに両方の値を手で揃え直す必要が出てきます。

割引キャンペーンを担当するマーケティングチームは、04の割引バッジのように定価と割引価格の差からパーセントを自分で求める仕組みをよく探します。担当者が計算した数字をそのままテキストに書き込むと、キャンペーン内容が変わるたびにずれる危険が残るからです。

従量課金制を売るB2Bチームでは、07の法人窓口CTAのように入力した利用量からその場で見積もりを求めるセクションが商談への転換率を左右します。営業担当が毎回手作業で見積もりを送る代わりに、訪問者自身がおおよその費用感をつかめるようにするのがこのセクションの役割です。

3つのバリエーションで確かめるreduced-motionの効き方

チラシ 価格表 デザインのような紙のレイアウトと違い、この9種は同じ見た目のまま定数を1つ変えるだけで体験の重さが変わります。9種の実装はgdcg4wsbで開くzipにまとめてあり、vanillaフォルダには9項目それぞれのindex.htmlが、reactフォルダには同じ計算をコンポーネント化したtsxが入っています。

名前 値 感じ方
割引バッジ緊急しきい値 04 URGENT_RATIO .25 → .5 残り時間が25%を切ってから警告色になると最後の瞬間だけ緊迫感が生まれ、.5に上げると半分の時点から急かされるが警告が常態化して効きにくくなる
法人CTA切替しきい値 07 THRESHOLD 1000 → 500 1000で切り替えると自分で見積もりを出せる幅が広く保たれ、500に下げると早めに営業窓口へ誘導できる一方でスライダーの後半がすべて見積もりなしの区間になる
比較表の強調アルファ 02 --diff-alpha .12+.35 → .06+.20 .12から.47の幅は違いが目立つ分だけ表全体が濃く見えやすく、.06から.26に狭めると落ち着くがどの行が違うのか一目では分かりにくくなる

prefers-reduced-motionをどう扱うか

9項目ともwindow.matchMedia('(prefers-reduced-motion: reduce)')の結果をreduceという変数に持ち、真であればanimateValueのような数字の転がり演出を飛ばして最終値を即座に描画します。座標やテキストの計算そのものは省略しないため、動きだけが止まり結果は変わりません。

コピー&ペーストで動く理由

どの項目もCSS変数の名前・クラス名・関数名を外部ライブラリに頼らず素のJavaScriptとCSSだけで完結させているため、フォルダごとindex.htmlを開けばそのままコピー&ペーストで動きます。

数字が配置を変える場所 — 割引率・利用量・レビュー評価

利用量がスライダーのleftとfillの幅を動かす

07法人窓口CTAではusageという1つの数字がknobのleftとfillのwidthという2つの座標をまとめて動かします。数字を直接CSSに書くのではなく、MAX_USAGEとの比率という計算結果を渡している点がポイントです。

画面幅がパートナーバーの表示個数を動かす

08パートナーロゴバーでは画面幅という外の数字がfitCountという内の数字を動かし、そのfitCountが今度は各ロゴのdisplayというレイアウトを動かします。数字が数字を動かし、その数字がレイアウトを動かすという二段構えです。

差がある行の数が強調の透明度を動かす

02比較表ではdiffCountという整数が--diff-alphaという小数に変わり、その小数がCSSの背景色の濃さになります。行が1つ増えるごとに濃さが均等に増えるのではなく、行数で割った比率で増えるため行数が変わっても濃さの上限は変わりません。

負の割引と桁あふれでどこから計算が壊れるか

割引バッジが負の%を表示するとき

04のバッジは(LIST引くSALE)をLISTで割った値をそのまま計算するため、将来の価格更新でSALEがLISTを上回るとpctが負の値になり「マイナス12%OFF」のような表示が起きえます。呼び出す前にSALEがLISTより小さいことを確かめる一行を足しておくと安全です。

レビューが0件でNaNになるとき

06のaverageはreviews.lengthが0だと0で割ることになりNaNが返ります。レビュー件数が0の状態を先に弾いて、代わりに「レビュー準備中」のような文言へ切り替える分岐が必要です。

利用量がMAX_USAGEを超えたとき

07のpctはusageをMAX_USAGEで割った値をそのまま使うため、入力欄などからMAX_USAGE 1500を超える値が渡るとpctが1を超え、knobLeftがトラックの外まではみ出します。usageをMAX_USAGEで丸めてから渡しておくと崩れません。

料金表セクションを組み込む前によくある質問

割引バッジのパーセントはどうやって決まりますか

「価格表 デザイン おしゃれ」で探す人の多くは色や余白だけを気にしますが、このバッジのパーセントは定価と割引価格の差を定価で割った計算結果です。定価49・割引価格32なら35%という数字がその場で求まり、見た目の装飾とは別に毎回同じ式が動いています。

法人窓口セクションはどこまで自動で見積もりを出しますか

入力した利用量が1000以下のあいだは単価を掛けた見積もり金額を即座に表示しますが、1000を超えると計算そのものをやめて営業チームへの案内文言に切り替わります。見積もりを出すか案内に切り替えるかの境目はTHRESHOLDという1つの定数で決まっています。

レビューの平均点はどこで計算していますか

表示側では何も計算せず、averageという関数が渡されたレビュー配列の評価をその都度合計して件数で割った値をそのまま画面に渡しています。レビューの組み合わせが変わればこの関数が呼び直され、平均も自動でつじつまが合います。

もっと読む

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

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