jsでピンチ操作(touchmoveでrotateとかスケーリング)

16/06/2020

コロナの影響もあってか
公開の延期とかペンディング
なんかがチラホラある今日このごろです。

結構急ぎの対応になる予定だったのだけども
延期になってちょっと時間ができたので
実装進行中のjsで画像の拡大とかスケーリングする
仕組み的な物記事にしてみます。

gesturechange

gesturechangeに対応してればscaleとかrotateって簡単にとれますが
iOS以外はサポートしてない?ような覚えがあるので
アンドロイドとかそのあたり実装の参考になりましたら幸いです。

とりあえずこんなの

実際案件として作ってるのは移動したりレイヤー複数だったりするのですが
あまり色々書くとわかりづらくなると思うのでPC省きでスマホ動作の拡縮と回転のみです。
あとReacttypescript使ってますが、特に別でmoduleとか使ってないので
各処理は素のjsとかでも読み変えてもらえると思います。

See the Pen touchmoveでrotation by admin@nocebo.jp (@nocebojp) on CodePen.

あとはちょい説明

Pointクラス

class Point {
  x: number;
  y: number;
  constructor(x: number = 0, y: number = 0) {
    this.x = x || 0;
    this.y = y || 0;
  }
  static distance(a: Point, b: Point) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
  }
  static interpolate(p1: Point, p2: Point, t: number) {
    let x = p1.x * (1 - t) + p2.x * t;
    let y = p1.y * (1 - t) + p2.y * t;
    return new Point(x, y);
  }
  add(p: Point) {
    this.x += p.x;
    this.y += p.y;
    return this;
  }
  subtract(p: Point) {
    this.x -= p.x;
    this.y -= p.y;
    return this;
  }
}

distanceはそのまま距離求める関数です。
指2本間の距離
三平方の定理のハズ。

interpolateもそのままですが2点間の補間点です。
今回は指2本の中間点とるのに使ってます。

touch系の処理

disableTouch

useEffectで使ってます。
userscalableオフにするやつ

passive: falseっていれないと
最近のiOSでは効かない。

touchPosition

タッチしてる指の本数分(e.touches)を配列で返します。
Object.entriesmapってのを使ってみた。

touchDeg

角度求めるヤツです。
上で出たinterpolate使って
指2本の補間点(中心)と
逆正接関数(Math.atan2)ってのを使って指1との角度を求める。

というつもり。であってるハズ。
Math.atanってのもあるので注意です。こんなの学校でならったかしら

コンポーネント

あとはコンポーネントで色々ハンドラとか書く。
自己流ReactなのでuseCallbackとかuseMemoとかのメモ化なんか
特に雰囲気で使ってるけどあってるかな?あってるといいな。
焼き肉が食べたいな。

一応回転とスケーリングの処理だけ補足。

rotate

回転

const currentPoints = touchPosision(e);
// それぞれの指の位置取得
const currentDeg = touchDeg(currentPoints);
// 指1と指2から角度取得
const diffDeg = currentDeg - startDeg;
// touchstart時の角度とmove時の角度の差を取得
const result = tempRotate + diffDeg;
// 要素自体のtouchstart時のrotate+差分

です。

scale

拡縮

const startDistance = Point.distance(startPoints[0], startPoints[1]);
// スタート時の指2本の距離
const currentDistance = Point.distance(currentPoints[0], currentPoints[1]);
// move時の指2本の距離
let scale = (currentDistance / startDistance) * tempScale;
// 現在距離/スタート時の距離 * 拡縮要素タッチ時の元のスケール
scale = Number.isNaN(scale) ? 1.0 : scale;
// ここは保険
scale = Math.max(scale, 0.5);
scale = Math.min(scale, 4.0);
// 最小スケールと最大スケール

です。

その他の処理

あと多分拡縮・回転だけってのは実案件だとそんなにないと思うので
移動だったりPCも対応もしたいって場合は
過去記事の下記なんか参考になるかもです。

参考になりましたら幸いでございます。

javascript, web

Posted by admin