2017年6月22日木曜日

re-natalでジョイスティックみたいなボタンを作る方法


実装方法が分かるまで迷うことが多かったので、方法を大雑把に共有します。
re-natalに関する記事ですが、react-nativeで作られる場合も参考になる部分があると思います。

使ったもの

  • re-natalで作っているアプリ
    re-natalとはreact-nativeをclojurescriptで開発できるフレームワークです。
    re-natal-esp32control-appを開発しています。
  • 上記アプリの開発環境
    $ re-natal --version
    0.4.0
    $ react-native -v
    react-native-cli: 2.0.1
    react-native: 0.43.4
    

コード

ジョイスティックのコードです。
触れた場所によって、左上だと {:x -1 :y -1}、中心だと {x: 0 :y 0}、右上だと {:x 1 :y 0}のような値を、第1引数のon-moveに渡して実行します。
画面から手を離すと、第2引数のon-releaseを実行します。

(def ReactNative (js/require "react-native"))
(def view (r/adapt-react-class (.-View ReactNative)))
(def image (r/adapt-react-class (.-Image ReactNative)))

(defn rate-x-y [p-x p-y view-x view-y view-w view-h]
  (let [harf-w (/ view-w 2)
        harf-h (/ view-h 2)
        rate-x (/ (- p-x view-x harf-w) harf-w)
        rate-y (/ (- p-y view-y harf-h) harf-h)]
    {:x rate-x :y rate-y}))

(defn joystick [on-move on-release]
  (let [view-x (r/atom nil)
        view-y (r/atom nil)
        view-w 300
        view-h 300
        joystick-ref (r/atom nil)
        update-view-x-y #(.measure @joystick-ref (fn [fx fy w h px py]
                                                   (reset! view-x px)
                                                   (reset! view-y py)))
        action (fn [evt]
                 (-> (rate-x-y (.-pageX (.-nativeEvent evt))
                               (.-pageY (.-nativeEvent evt))
                               @view-x @view-y view-w view-h)
                     (on-move)))]
    (r/create-class
     {:reagent-render
      (fn []
        [image
         {:source joystick-img
          :style {:resize-mode "cover"
                  :background-color "#beb"
                  :align-self "center"}}
         [view
          {:ref "joystickArea"
           :style {:width view-w
                   :height view-h}
           :on-layout #(update-view-x-y)
           :on-start-should-set-responder (fn [] true)
           :on-move-should-set-responder (fn [] true)
           :on-responder-grant #(action %)
           :on-responder-move #(action %)
           :on-responder-release #(on-release)}]])

      :component-did-mount
      #(reset! joystick-ref (-> (.-refs %)
                                (.-joystickArea)))})))

(defn joystick-action [{:keys [x y]}]
  (prn :x x)
  (prn :y y))

(defn [] some-page
  ..
  [joystick joystick-action #(joystick-action {:x 0 :y 0})])

参考コミット
Add joystick
Add image for joystick

実装時の注意点: androidにおけるlocationXの不具合

react-native0.43.4のandroid環境では、on-responder-moveなどのイベントに含まれるnativeEventのlocationXとlocationYが更新されない不具合があります。

[Android] onResponderMove nativeEvent locationX does not change

そのため、joystickの左上の座標をon-layoutで取得し、それを使ってpageXとpageYからlocationXとlocatoinYに相当する値を計算しています。

まとめ

viewのresponcer-moveを利用として、ジョイスティックを実現しました。
locationXに関する不具合が解決すれば6行くらいコードを短くできますが、とりあえず欲しい機能を作れたので満足です。

共有する情報は以上です。

参考

Gesture Responder System
ReactNativeソースコードリーディング <View>
View
React-Native Tinder like cards
React Native: Getting the position of an element
[Orientation] Re-Layout on orientation change

0 件のコメント :