スマホは PC の縮小版ではなかった

PC の画面を縮めただけだったスマホ版を作り直した記録。スマホの判定を1か所に決め、PC の数字を書き換えず、2本指の操作を両方の経路で成り立たせるまで。

#スマホ#ジェスチャ#React Three Fiber

9月の半ば、このサイトのスマホ版を作り直しました。もともとは、PC 向けに作った画面を、Tailwind CSS の画面幅の切り替えで縮めていただけでした。実際、スマホで見る前提の分岐は、コードの中に1つも無かったのです。

最初に頼んだのは「スマホの画面を洗練したい」でした。ただ、推測で直し始める前に、まず iPhone 14 と同じ大きさ(390×844)の画面で、何が起きているかを測ってもらいました。

測ったら、思った以上に壊れていた

結果は、ひとことで言うと「PC の縮小版は、スマホでは成り立っていない」でした。

  • カードが被写体を完全に隠す。 PC では、画面の左にカードを置いて、カメラをその分だけ右にずらして建物を映しています。「カードは画面の3割くらいしか使わない」という前提です。でもスマホでは、カードが画面の幅の86%を占めていました。カメラを3割ずらしても、建物はカードの別の部分の裏に移るだけでした
  • 画面の下で、ボタンが重なる。 リンクのボタン、ホームへ戻るボタン、フッターの文字が、同じ帯に重なっていました
  • フッターが見切れる。 「Privacy Policy」が、画面の右端で「Privacy Polic」と切れていました
  • 自己紹介の文字が、指で動かない。 500ピクセル指を滑らせて、進んだのは73ピクセルでした

最後の1つが一番厄介でした。自己紹介は、映画のオープニングのように文字が奥へ流れていく演出で、スクロールの位置を「目標の位置」として持ち、毎フレームそこへ滑らかに追いかける仕組みになっています。マウスのホイールには専用の処理があって、目標の位置を動かしていました。でも指でのスクロールは、ブラウザが直接スクロール位置を書き換えます。それを、次のフレームで追いかけの仕組みが「目標」へ引き戻してしまう。エラーも出ず、ただ文字が動かないという、無言の失敗でした。

スマホかどうかを、1か所で決める

そこから7つの変更をお願いして、作り直しが始まりました。停止ボタンは「空をタップしたら止まる」に、ズームのボタンは2本指のピンチに、カードは画面の下を横に流れるレールに。ボタンを指のジェスチャに置き換えていく作り直しです。

ボタンを消してジェスチャに置き換えるのは、CSS では書けません。そこで、「今スマホで見ているか」を JavaScript で判定する必要が出てきました。判定の条件は、「指で操作する画面」かつ「幅が1023ピクセル以下」です。両方が必要でした。

  • 「指で操作する」だけだと、タッチパネル付きのノートパソコンからボタンを奪ってしまう。マウスもあるのに、ピンチする相手がいない
  • 「幅が狭い」だけだと、デスクトップのブラウザを細くしただけの人からボタンを奪ってしまう。代わりのジェスチャが、そもそも使えない

そして、この判定はプロジェクトの中でただ1か所に置き、2つ目の判定を作らないことにしました。画面の部品とカメラの部品が別々に「スマホかどうか」を判断すると、カードはスマホ用なのにカメラは PC の構図、という組み合わせが起こりうるからです。

PC の数字を、スマホの都合で変えない

作り直しの中で、同じ種類の失敗が2回起きました。

1回目は、カメラの構図です。スマホではカードが画面の下に来るので、「惑星を上に持ち上げて、下を空けよう」という計算で値が決まりかけました。計算の筋は通っていたのですが、前提が違いました。スマホの標準の高さでは、惑星は「画面の中の円盤」ではなく、画面いっぱいに広がる風景です。実際に撮ってみると、地面が画面の94%を覆って、水平線が画面の上に消えていました。そこでは、惑星を置く位置ではなく、水平線をどこに入れるかを決める必要があったのです。

2回目は、「俯瞰の視点をもう少し俯瞰に。標準の視点も、画面の3分の1は背景が見えるように」と頼んだときです。僕の頭の中ではスマホの話だったのですが、依頼文にそれを書いていませんでした。その結果、PC とスマホの両方に効く数字が変えられてしまいました。PC の数字は、参考写真から導いた値です。スマホの都合で動かしていいものではありません。PC を元に戻して、スマホ専用の数字を別に足す形でやり直してもらいました。

2つの失敗に共通していたのは、「どのフレームの話なのか」を取り違えたことです。1回目は惑星を円盤として扱う遠景の考え方を、風景として見える近景に当てはめてしまい、2回目はスマホの話を PC にまで広げてしまいました。そこから残ったルールは、「どの画面の話か」は、依頼文に書かれていなくても確定させてから触る、ということです。PC は横長で、スマホは縦長で下にカードのレールがある。フレームの形が違えば、正しい構図の数字も違います。そしてこのプロジェクトでは、PC 用の数字を「スマホで具合が悪い」という理由で書き換えず、スマホ用の数字を別に持つことにしています。

「背景を画面の3分の1」は、最後にはスマホの2つの高さを両方とも解く、1つの規則になりました。惑星の縁が、画面の上から3分の1の位置に来る。そこから逆算して、標準の高さは50から77.9に、俯瞰は100から122.8になりました。実測すると、標準の視点で空が占める割合は、画面の15%からちょうど3分の1になっていました。

同じ「2本指」でも、届き方が違う

一番混乱したのは、2本指の操作でした。報告した症状は、PC とスマホで結果が逆になる、というものです。

  • 2本指のピンチ(ズーム)は、スマホの実機では効くのに、PC では効かない
  • 2本指のスクロール(惑星を回す、自己紹介を送る)は、PC では効くのに、スマホの実機では効かない

原因は、同じ「2本指」でも、ブラウザへの届き方がまったく違うことでした。

  • PC のトラックパッドのピンチは、指の位置のイベントではなく、ホイールのイベントとして届く(Ctrl キーを押しながらのホイール、という扱い)。ピンチを「指が2本触れたら」で待っていたので、PC では永遠に発火しませんでした。しかも普通のホイールとして処理されて、ピンチするとツアーが進むという、反応しないより悪い状態でした
  • スマホには、ホイールのイベントが無い。 ツアーを進める処理も、自己紹介のスクロールも、ホイールにしかつながっていなかったので、指では動かしようがありませんでした

直し方は、どちらも「もう片方の経路を足す」ことでした。PC ではピンチのホイールをズームとして拾い、スマホでは2本指の動きを見て、指の間隔が変わればズーム、一緒に動けばスクロール、と分けるようにしました。

この「ズームかスクロールか」の判定にも落とし穴がありました。片手でスマホを持って、親指を固定したままピンチすると、指の間隔が変わるのと同時に、2本の指の真ん中も半分だけ動きます。「先に閾値を越えたほう」で判定すると、ピンチの途中でスクロールだと誤解してしまう。そこで、間隔の変化と、真ん中の移動量を直接比べて、大きいほうを採ることにしました。

スマホは、別の場面

この作り直しを通して、スマホは PC の縮小版ではなく、フレームの形も、下端をふさいでいるものも、指の届き方も違う、別の場面だと考えるようになりました。

PC の構図は参考写真から決めた数字で、スマホの縦長の画面については何も言っていません。スマホで具合が悪いのは、PC の数字が間違っていたからではなく、その数字が想定していなかった場面だからです。だから、PC の数字を書き換えるのではなく、スマホの数字を別に持つ。

そして、ジェスチャを1つ足したら、PC とスマホの両方の経路で確かめる。片方だけで「実装した」と思い込むと、もう片方で反応しないまま出してしまいます。上の2本指の件が、まさにそれでした。

このノートに出てくるプロダクト

ほかのノート