react-resizable使った話

pgmot.hatenablog.com

これの続きです。

研究で色々あってreactを使う判断をしました。

研究ではAndroidのセンサログ情報やら開発者が実装した行動認識を行うクラスを可視化して想定通りに行動認識アルゴリズムが動作しているかの調査や、そもそもどういったセンサデータが用いれるかを調べられるWebベースのデバッグツールを作っていました。 本デバッグツールでは様々なセンサデータを可視化することとなるのですが、その際には、グラフなどを自由にサイズ変更できる必要があるかと思います。

そこで STRML/react-resizable: A simple React component that is resizable with a handle. を使いました。 名前の通りリサイズに対応したReactコンポーネントです。

インストール方法とインポート方法はREADMEを参照。

function onResize(e, {element, size}){
  this.setState({
    viewSize: size
  });
}

<ResizableBox width={this.state.viewSize.width} height={this.state.viewSize.height} onResize={onResize}>
  <div>
     Resizable
   </div>
</ResizableBox>

簡単ですね。

ただリサイズできるようにするためのハンドルも設定しないといけません。

    .react-resizable {
      position: relative;
    }
    .react-resizable-handle {
      position: absolute;
      width: 20px;
      height: 20px;
      bottom: 0;
      right: 0;
      background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=');
      background-position: bottom right;
      padding: 0 3px 3px 0;
      background-repeat: no-repeat;
      background-origin: content-box;
      box-sizing: border-box;
      cursor: se-resize;
    }

cssにこれを書く必要があります。backgroundには 型の画像が仕込まれていてここをドラッグすることでリサイズできるような仕組みです。

これもreact-draggableのときと同じようにコンポーネントのサイズを保持したい場合はlocalStorageなどを活用すればよいでしょう。

this.state = {
  viewSize: {
    width: parseInt(localStorage.getItem("width"), 10) || 400,
    height: parseInt(localStorage.getItem("height"), 10) || 400,
  }
};

function onResize(e, {element, size}){
  this.setState({
    viewSize: size
  });

  localStorage.setItem(`width`, size.width);
  localStorage.setItem(`height`, size.height);
}

<ResizableBox width={this.state.viewSize.width} height={this.state.viewSize.height} onResize={onResize}>
  <div>
     Resizable
   </div>
</ResizableBox>

さらにreact-draggableと連携すればドラッグ可能でリサイズ可能なウインドウっぽい何かが完成します。

// constructorなどで実行
this.state = {
  componentPosition: {
    x: parseInt(localStorage.getItem(`position_x`), 10) || 0,
    y: parseInt(localStorage.getItem(`position_y`), 10) || 0
  },
  viewSize: {
    width: parseInt(localStorage.getItem("width"), 10) || 400,
    height: parseInt(localStorage.getItem("height"), 10) || 400,
  }
};

function onDrag(e, position){
  const {x, y} = position;
  this.setState({
    position: {x, y}
  });
}

function onStop(e, position){
  const {x, y} = position;

  localStorage.setItem("position_x", x);
  localStorage.setItem("position_y", y)
}

function onResize(e, {element, size}){
  this.setState({
    viewSize: size
  });

  localStorage.setItem(`width`, size.width);
  localStorage.setItem(`height`, size.height);
}

<Draggable handle="handle" position={this.state.componentPositon} onDrag={onDrag} onStop={onStop}>
  <div className="handle">
    ここをドラッグ!
  </div>
  <ResizableBox width={this.state.viewSize.width} height={this.state.viewSize.height} onResize={onResize}>
    <div>
       Resizable
     </div>
  </ResizableBox>
</Draggable>

こういうことをしてなんとか修論を乗り越えました。裏ではWebsocketやらReduxやら色々使っているので元気が出たらそのあたりも頑張って記事にします。