マガジン

小さい画面でボタンが押せない:タッチ領域と見た目の差

遊びをつくる

執筆にClaudeを使用しています。

大きな操作ボタンと横向きの砂時計、複数の操作道具が並ぶ紙と木のゲーム風景
記事のテーマをもとに制作したAI生成イラスト。

画面の左下に、左右へ動かすための小さな矢印ボタンが2つ並んでいるゲームを考えてみます。見た目は整っていて、PCのマウスで押すぶんには何の問題もありません。ところがスマートフォンで親指を乗せると、左を押したつもりが反応しなかったり、隣の右が動いたりします。遊ぶ人は「このゲームは反応が悪い」と感じて閉じてしまい、作った人は原因に気づかないまま終わります。 この症状の原因は、ボタンの「見た目の大きさ」と「指に反応する範囲」を同じものとして扱っていることにあります。この二つは別々に決められます。見た目は小さいまま、反応する範囲だけを広げることもできます。この記事では、その方法をコードで示し、広げすぎたときに起きる問題と、自分のゲームで効果を確かめる手順まで順に説明します。

この記事の目次
  1. 見た目の大きさと反応する範囲は別のもの
  2. 疑似要素で反応範囲を広げる
  3. 隣り合うボタンでは広げすぎない
  4. 自分のゲームで効果を確かめる手順
  5. 公式作品の設計から読み取れること
  6. 次の行動
  7. 参考資料

見た目の大きさと反応する範囲は別のもの

まず、混同しやすい三つの大きさを分けておきます。

  • 見た目の大きさ:画面に描かれる絵や枠の大きさです。
  • 反応する範囲:指やポインタが触れたときにイベントが発生する領域です。HTMLのボタンなら、通常は要素の矩形(四角形)がそのまま反応範囲になります。
  • 指の接触面:実際に画面に触れる指先の面です。マウスの先端よりずっと大きく、狙った点からもずれます。

小さい画面で押しにくくなる理由は、指の接触面が大きくてずれるのに、反応する範囲が見た目と同じ小ささのままだからです。見た目を大きくすれば解決しますが、画面の狭いゲームでは、ボタンを大きくするぶん盤面や操作対象が隠れます。そこで、見た目は変えずに反応範囲だけを外側へ広げる、という考え方が役に立ちます。

目安になる数字は、一般に公開されている指針にあります。W3CのWCAG 2.2では、達成基準2.5.8(レベルAA)でポインタ操作の対象を少なくとも24×24 CSSピクセルとし、2.5.5(レベルAAA)では44×44 CSSピクセルを求めています。AppleのHuman Interface Guidelinesは44×44ポイント、Material Designは48dpを目安としています。数字の細かい条件や例外は各資料で確認してください。ゲームの操作部分は一般的なウェブページの規則がそのまま当てはまるとは限りませんが、目安として手元に置いておく価値はあります。

疑似要素で反応範囲を広げる

最も手軽な方法は、ボタンに透明な疑似要素(::before)を付け、ボタンの外側へはみ出させることです。疑似要素はボタンの一部として扱われるため、はみ出した部分を触れてもボタンが反応します。

次のコードは単独で動く最小の例です。見た目が32px四方のボタンを、反応範囲だけ48px四方にします。

html
<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{margin:0;padding:40px;touch-action:manipulation}
.row{display:flex;gap:16px;justify-content:center}
.pad{position:relative;width:32px;height:32px;border:0;border-radius:8px;background:#456;color:#fff}
.pad::before{content:"";position:absolute;inset:-8px}
.debug .pad::before{background:rgba(255,0,0,.25);outline:1px dashed red}
</style>
<div class="row">
  <button class="pad" data-d="-1">◀</button>
  <button class="pad" data-d="1">▶</button>
</div>
<p id="out">押してみてください</p>
<script>
const out=document.getElementById("out");
let x=0;
if(location.search.includes("debug"))document.body.classList.add("debug");
document.querySelectorAll(".pad").forEach(b=>{
  b.addEventListener("pointerdown",e=>{
    const r=b.getBoundingClientRect();
    const dx=Math.round(e.clientX-(r.left+r.width/2));
    const dy=Math.round(e.clientY-(r.top+r.height/2));
    x+=Number(b.dataset.d);
    out.textContent="位置 "+x+" / 中心からのずれ "+dx+","+dy;
  });
});
</script>

ポイントは次の四つです。

  1. .pad::before の inset:-8px で、ボタンの四辺から8pxずつ外側へ広げています。32pxに両側8pxを足して、反応範囲は48pxになります。
  2. 疑似要素の背景は透明のままなので、見た目は変わりません。
  3. ボタンの中心を基準にしたずれは、getBoundingClientRect() で得られます。この矩形は疑似要素を含まない見た目の矩形です。そのため、押した点が見た目の外にあるのに反応した、という状況も数字で読めます。
  4. アドレスの末尾に ?debug を付けて開くと、広げた範囲が赤く表示されます。範囲を目で確かめるための仕掛けで、本番では不要です。

touch-action:manipulation は、ダブルタップによる拡大などを抑えて、ボタンの連打で画面が拡大されるのを防ぐための指定です。ゲームの操作ボタンでは入れておくと安心です。

なお、ブラウザや端末によって、ボタン内の疑似要素の扱いに差が出る可能性はあります。必ず実機で動作を確かめてください。

隣り合うボタンでは広げすぎない

反応範囲を広げる方法には落とし穴があります。広げた範囲が隣のボタンの範囲と重なると、重なった場所では、DOM上で後ろにあるボタンが優先されます。左ボタンの右端を押したつもりが、右ボタンの反応になる、という現象です。

先ほどの例で gap:16px としているのは、これを避けるためです。各ボタンが外側へ8pxずつ広がるので、隣り合うボタンの反応範囲は、ちょうど16pxの間隔の中央で接します。見た目の間隔と反応範囲の関係は次のように考えられます。

  • 広げた幅の合計が間隔より大きい、起きること:重なった部分は後ろのボタンが優先される、対処:広げる幅を間隔の半分以下にする
  • 広げた幅の合計が間隔と同じ、起きること:境目で接する。隙間は残らない、対処:多くの場合の落としどころ
  • 広げた幅の合計が間隔より小さい、起きること:反応しない隙間が残る、対処:隙間に指が落ちやすいので広げ足す
  • ボタンが画面の端にある、起きること:端の外側は広げても意味がない場合がある、対処:内側へ寄せて配置する

画面の端は特に注意が必要です。端に近い部分は、端末やOSの操作(端からのスワイプなど)と競合する場合があります。端末ごとに違うため、重要なボタンは端から少し離して置くことをおすすめします。

もう一つの注意点は、反応のタイミングです。pointerdown(触れた瞬間)で動かすと遅れが少なく、ゲームの手触りは良くなります。その代わり、押し間違えても取り消せません。click(離した瞬間)を使うと、触れたあとに指をずらして取り消せます。素早い操作が要るゲームではpointerdown、押し間違いが痛いゲームではclickというように、ゲームの性質で選んでください。

自分のゲームで効果を確かめる手順

広げた範囲が効いているかどうかは、感覚に頼らず記録で確かめることができます。ここでは「結果を示す」のではなく、読者自身が行える実験の設計を示します。

問い:反応範囲を見た目より広げると、押し間違い(反応しない、または隣が反応する)は減るか。

準備するもの

  • 実際に遊ぶ予定の端末(できれば画面の小さいもの)。
  • 上のコードか、自分のゲームに記録用の表示を足したもの。
  • 記録用の紙か表。
  • 比較用に、inset を 0 にしたバージョンと、-8px にしたバージョン。

手順

  1. 端末を普段の持ち方で持ちます。両手持ちや片手持ちなど、持ち方を決めて記録に書きます。
  2. 「左を押す」「右を押す」を交互に10回ずつ、合計20回行います。狙うボタンは、押す前に決めておきます。
  3. 各回について、「狙ったボタンが反応したか」「隣のボタンが反応したか」「何も反応しなかったか」の3つのどれかを記録します。
  4. 画面に表示される「中心からのずれ」も控えておきます。
  5. inset の値を変えたバージョンで、同じ手順を繰り返します。持ち方と回数は揃えます。
  6. 可能なら、別の持ち方や別の人でも行います。人によって指の大きさも癖も違います。

結果の読み方

  • 見た目の矩形の外を押したのに狙いどおりに反応した回数が増えていれば、広げた範囲が機能しています。
  • 隣のボタンが反応した回数が増えているなら、広げすぎ、または間隔不足です。inset を小さくするか、見た目の間隔を空けてください。
  • 何も反応しなかった回数が多く、ずれの値が一方向(たとえばいつも下側)に偏っているなら、範囲を均等に広げるより、偏った方向を広げるほうが効く可能性があります。inset:-8px -8px -12px -8px のように、辺ごとに値を変えられます。

起こりやすい誤り

  • 回数が少なすぎる:数回では偶然の影響が大きいので、各条件で最低でも十数回は取りたいところです。
  • 条件を同時に変える:広げる幅と間隔を同時に変えると、どちらが効いたのかがわからなくなります。一度に一つだけ変えてください。
  • 開発中のPCのブラウザでだけ確かめる:マウスは指よりずっと正確です。実機のタッチでの確認が欠かせません。
  • 慣れの影響:同じ人が何度も試すと、位置を覚えて成績が上がります。順番を入れ替えるか、休みを挟んでください。

公式作品の設計から読み取れること

アソバースの公式作品の確認済みの事項からも、押しやすさへの配慮を読み取れる部分があります。ただし、ボタンの具体的な大きさや反応範囲の数値は確認済み事項にないため、ここでは述べません。その部分は作品を開いて確かめてください。

「紙ひこうき郵便」は、左右キー、ドラッグ、矢印ボタンの三通りで操縦できます。矢印ボタンが小さく感じられても、画面をドラッグして動かす別の手段があります。入力手段を複数用意すると、小さい画面で一つの手段が押しにくくても、遊べなくなる事態を避けられます。矢印ボタンの大きさや、ドラッグが効く範囲は、作品を開いて指で試してください。

「WAVE / WAVE」は16ステップを並べるリズムスタジオで、狭い画面ではステップが横スクロールになります。16個を狭い画面に収めるために縮小すると、一つずつが小さくなり、指で押し分けられなくなります。ステップを縮めずに横スクロールにする仕組みは、押せる大きさを優先した設計と読み取れます。実際にどの程度の大きさで表示されるのかは、狭い画面で開いて確かめてください。

「潮目のオセロ」は矢印キーとEnterで操作できます。タッチ以外の入力経路を用意しておくことも、操作の選択肢を増やす設計の一つです。

また、13作品は共通して、プレイ画面を拡大しても進行が保たれるようになっています。画面を拡大したときに、ボタンの大きさや反応範囲がどう変わるかは、作品ごとに開いて確かめてください。自作のゲームでも、拡大や縮小で操作が壊れないか確認することをおすすめします。

投稿する側として知っておきたい点もあります。アソバースでは、アップロードしたゲームはsandbox付きのiframeの中で動きます。投稿は「入力→表示の確認→公開」の3段階で、公開前に表示を確認できます。この表示確認の段階で、スマートフォンから実際に開き、ボタンが指で押せるかを試しておくと、公開後の「反応が悪い」という印象を避けられます。

次の行動

自分のゲームで一番小さい操作ボタンを一つ選び、見た目の大きさを測ってください。そのボタンに透明な疑似要素を付けて反応範囲を48px前後に広げ、?debug のような表示で範囲を可視化します。そのうえで、隣のボタンとの間隔を確認し、実機で20回ほど押して、反応した・隣が反応した・反応しなかったの3つを数えてみてください。

参考資料

  • W3C「Web Content Accessibility Guidelines (WCAG) 2.2」達成基準 2.5.8 Target Size (Minimum)、2.5.5 Target Size (Enhanced)
  • Apple「Human Interface Guidelines」
  • Google「Material Design」のタッチターゲットに関するガイドライン