はじめに
ReactでSVG要素を書いてそれをドラッグしたくなったのですが、自分でonMouseDown()
とか書くのは嫌だなぁと思いました。
何度かやったことがありますが細かいところでめんどくさいです。マウスダウンしたまま、ウィンドウの外に出した場合とか、移動しすぎ、拡大縮小しすぎた場合とか、やってみるとたくさんあるんです。
こんなの誰かがモジュール作ってくれてるでしょうと思ったらやはりありました。今回、このreact-zoom-pan-pinch
の使い方をさらっと説明します。インストールとか割愛。
今回の私のコードは下記。(canvasって名づけてしまったけどsvgです)
使い方
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
import "./MySvg1.css";
const MySvg1 = () => {
return (
<div>
sample1. TransformComponentの中にsvgタグ
<TransformWrapper>
<TransformComponent>
<svg width={400} height={400} >
<rect x="50" y="50" width="100" height="100" fill="blue" />
<circle cx="250" cy="150" r="50" fill="red" />
<text x="50" y="250" fontSize="20" fill="green">Hello, SVG!</text>
</svg>
</TransformComponent>
</TransformWrapper>
</div>
);
};
<TransformWrapper>
タグと、<TransformComponent>
タグで括った中の要素が、全体的にドラッグできるようになります。それだけ。
画像を貼ってもあまりありがたみはありませんが、こんな感じです。境界を見たかったので、背景色をちょっと変えてます。
ちょっとした注意点1
上の書き方をすると、<svg width={400} height={400} >
がドラッグできる状態になるので、ドラッグすると<svg>
の角が見えるんですね。<svg>
に背景色をつけていると、その角が見える状態です。
公式のサンプル(Examples / SVG Zoom To Element)ではそんな状態ではなかったので、よーく見ると、<TransformWrapper>
から生成されていると思われる<div>
にclass="react-transform-wrapper"
があって、.react-transform-wrapper
で色を付けている。
ということで回避策は、自分のcssに、.react-transform-wrapper
の背景色を設定することでした。
.react-transform-wrapper {
background-color: #edd;
}
これでピンクっぽい色がついてます。
(↑わかりづらいけど回避策実施後。右端もちゃんと切れてOK。)
ちょっとした注意点2
SVGのwidth(400)とheight(400)より、外の要素を作ってみました。<rect x="350" ...
と<circle x="460" ...
です。
<TransformWrapper>
<TransformComponent>
<svg width={400} height={400} >
<rect x="50" y="50" width="100" height="100" fill="blue" />
<circle cx="250" cy="150" r="50" fill="red" />
<text x="50" y="250" fontSize="20" fill="green">Hello, SVG!</text>
<rect x="350" y="50" width="200" height="100" fill="lightblue" />
<circle cx="460" cy="150" r="50" fill="green" />
</svg>
</TransformComponent>
</TransformWrapper>
ドラッグしてみると・・・↓
ないことになってる。(ように見える)
そうか。ドラッグによって<svg>
ごと移動されてるから、表示されてないんですね。<svg>
の右下が見えているということ。上の注意点1をやったがために、消えたと勘違いしました。
対策は"ちょっとした"注意点ではなくなる予感がしますね。ここは"ちょっと"で抑えるために、力業発動。スマートな別解があるかもしれません。
対策1: <svg>
を広げる
まず、<svg>
要素を、400x400だったのを、550x400に広げました。このサイズは、動的に自分で計算するか、広めにとっておくかどちらか。ここが力業たるゆえんです。
<svg width={550} height={400} style={{backgroundColor: "#dde"}} >
<rect x="50" y="50" width="100" height="100" fill="blue" />
<circle cx="250" cy="150" r="50" fill="red" />
<text x="50" y="250" fontSize="20" fill="green">Hello, SVG!</text>
<rect x="350" y="50" width="200" height="100" fill="lightblue" />
<circle cx="460" cy="150" r="50" fill="green" />
</svg>
※ ついでに、<svg>
の範囲がわかるように、style
で水色#ddeを付けてます。本番では不要。
横幅を400から550にしたので、全部表示されます。でも横長になります。
でも400x400にしたい!ということで・・・
対策2: <TransformWrapper>
の幅を400にする
背景色を設定するために使ったcssをもう一度使います。
.react-transform-wrapper {
background-color: #edd;
width: 400px;
height: 400px;
}
こうすることで、
からの~ドラッグで
はい!きた!!
※ svgとその外の境界がわかるように、svgにつけた背景色のおかげで水色の角がありますが、設定を消せば見えません!
でっかい<svg>
の一部を表示してドラッグする機能というのは、ちょっと引っかかるけど、まぁ解の1つではあるからいいか。
ちなみに
ChatGPTさんに聞いたら、react-canvas
というモジュールもあるよって言われたから、調べたりあれこれ聞いてるうちに、古いから使わない方がいいよと言われました。2024年時点でLast publishが7年前。うん、まぁ確かに。ありがとう。でも、うん・・・はい。
おわりに
react-zoom-pan-pinch
は、この手のシステムでは全員必要なんじゃないでしょうか。紹介した以外にも、特定の要素をズームしたり、初期に戻したり、痒い所に手が届くモジュールです。
私も今後とも使い続けます。
svg-zoom-pan
というJavaScriptのライブラリがあって、ndmもあるんですが、そちらは調べきれてないです。svg限定だとしたら、そちらの方がよいのかもしれない。
でもJavaScriptの方はメンテされているけどndmは古かったり、react-zoom-pan-pinch
はドラッグした後のanimationがデフォルトであるけどsvg-zoom-pan
はなさそうとか、そのあたりが気になりました。ちょっとやりかけてやめた。そのうち調べるかも。
いいやり方などありましたら、コメント欄で是非教えてください!