見出し画像

iOS14の変形カーソルを、自前でゼロから実装するアルゴリズム

おおわく、実装方法が検討がついたのでメモ。


基本ロジック

ボタンと実マウスポインターの位置関係にあわせて、物理エンジンを用いて、バーチャルなマウスポインターをスプリングで引っ張って制御していると思われる。


1. ベースの当たり判定

ボタンの周辺には、サイズを拡張した仮想の当たり判定が存在する。当たり判定は、実ボタンだけでなく、この仮想アタリ判定で行う。

スクリーンショット 2020-06-26 19.40.03


2. 仮想ポインターの生成

マウスポインターが、ボタンの仮想アタリ判定に侵入したとする。このとき、マウスポインターの位置に、仮想マウスポインターを生成する。

スクリーンショット 2020-06-26 19.40.10


3. 仮想ポインターと物理エンジンの接続

仮想ポインターに対して、物理エンジンでスプリングを2つ生成し、接続する。接続先は、それぞれマウスポインタ、ボタンの中心。(スプリングは引力でも代替可能だが、初動の動きが速いのでスプリングと想定される)。

A. マウスポインタと、仮想ポインターの間に弱いスプリング(無荷重時全長0pxぐらい)。

B. 仮想ポインターと、ボタンのセンターの間に強いスプリング(
無荷重時全長0px)。

スクリーンショット 2020-06-26 19.40.15


4. 物理シミュレーション

2つのバネの力の非対称性から、仮想ポインターは、ボタンのセンターにたいして引き寄せられる。

スクリーンショット 2020-06-26 19.40.20


5. 距離による変形

マウスポインタとボタンの中心の距離に対する、仮想ポインターの現在位置から、interpolationで比率を計算する。ポインター上に仮想ポインタがあれば円形。ボタン中心に仮想ポインタがあればボタンの形に。移動中は、移動距離の比率に応じて、両シェイプのブレンド形状をとる。

スクリーンショット 2020-06-26 19.40.25


6. 仮想ポインターの位置の均衡

最終的に2つのスプリングの強度差から、だいたいボタンのセンター位置に達したところで、仮想ポインターの位置は停止する。ただし仮想ポインターは、マウスポインターとスプリング接続されているので、マウスポインターの位置に応じて、多少引っ張られた状態となる。

また、マウスポインターが完全にボタンの上にのったときは、マウスポインタと、仮想ポインターの間に弱いスプリングを解除し、完全にボタンにカーソルをスナップさせる。

スクリーンショット 2020-06-26 19.40.30


7. ボタンからボタンへのフォーカス移動

マウスポインターが、別ボタンの仮想アタリ領域にロールオーバーした場合、現在のスプリングを解除する。あわせて、新しいボタン・シェイプ・マウスポインターの3者間で、スプリングを貼り直す。

ただし、仮想ポインターの現在のシェイプは保存をし、新しいボタンオブジェクトで取るべき形と、ふたたび位置関係にあわせてシェイプをブレンドする。

スクリーンショット 2020-06-26 19.40.36

スクリーンショット 2020-06-26 19.40.42


たぶん、こんな感じ。

雑な見積もりとして、物理エンジン込みで、JSで2000-3000行、雑実装なら5-600行以内でいけそう。


<追記>

ざっくり書いたら、ざっくり動いたのでだいたいあってそう。チューニングすれば、ちゃんと動きそう感。



いただいたサポートは、コロナでオフィスいけてないので、コロナあけにnoteチームにピザおごったり、サービス設計の参考書籍代にします。