作ったもの
とりあえず、作ったものをはじめに載せておきます。
下記リンクから見れると思います。
サンプル
問題
ReactでSVGを扱う方法は色々あると思うが、使い勝手が悪いなと思う点が多かった。
例えば、
- jsxで使えるようにするためにSVGのプロパティ名を変更する必要がある
-
react-svg-loader
などを使いSVGファイルをComponentとして簡単に扱えるようにしても、カスタマイズが容易ではない
など。
そして個人的に一番実現したかったのが、
デザイナーがIllustrator等でSVGのデータを作ることがあるが、そのデータ自体には修正を加えることなく、かつブラウザ上では何か動きをつけたい
というもの。
例えば、以下のようなSVGファイルをIllustratorで作ってもらい、このSVGファイルを直接編集することなく、<image>
にエフェクトを加えたり、<text>
の内容を編集しようとすると案外手間がかかったりする。
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="TopLayer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2551.2 5102.4" enable-background="new 0 0 2551.2 5102.4" xml:space="preserve">
<g id="Image">
<image
overflow="hidden"
width="2710"
height="3189"
xlink:href="/img/sample.jpg"
transform="matrix(0.9414 0 0 0.9067 0 1324.6071)">
</image>
</g>
<g id="Background_1_">
<rect x="0" y="4214.9" fill="#B1B1B1" width="2551.2" height="887.5"/>
</g>
<g id="Background">
<rect x="0" fill="#B1B1B1" width="2551.2" height="1324.6"/>
</g>
<g id="Message">
<rect x="313.2" y="421.5" fill="none" width="1937.6" height="605.9"/>
<text transform="matrix(1 0 0 1 381.9751 597.4545)" font-family="'KozGoPr6N-Regular-90ms-RKSJ-H'" font-size="200px">あいうえおかきくけ</text>
</g>
<g id="Name">
<rect x="313.2" y="4355.1" fill="none" width="1937.6" height="605.9"/>
<text transform="matrix(1 0 0 1 481.9751 4531.1294)" font-family="'KozGoPr6N-Regular-90ms-RKSJ-H'" font-size="200px">こさしすせそたちつ</text>
</g>
</svg>
目標
先ほどのSVGファイルの、<g id="Image">
内の<image>
で表示されている画像をボタンから位置調整できるようにすることを目標とする。
方法
react-samy-svg
react-samy-svgを利用する。
react-samy-svg
は「Background」に記述されているが、
SVGファイルをjsxとして使うための作業(プロパティ名の変更など)や、既存のファイルを修正することなく、ReactコンポーネントとしてSVGを呼び出せ、かつカスタマイズできるというもの。
個人的にはselector
というプロパティでSVGのタグが指定できるのが強力だと感じた。
実践
react-samy-svg
をインストール
$ npm install react-samy-svg
以下、<image>
を位置調整するコード
import React from 'react';
import { Samy, SvgProxy } from 'react-samy-svg';
export default class SvgComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
translateX: 0,
translateY: 0,
};
}
handleClick(direction) {
switch (direction) {
case 'up':
this.setState({
translateY: this.state.translateY - 100,
});
break;
case 'down':
this.setState({
translateY: this.state.translateY + 100,
});
break;
case 'right':
this.setState({
translateX: this.state.translateX + 100,
});
break;
case 'left':
this.setState({
translateX: this.state.translateX - 100,
});
break;
default:
this.setState({
translateX: 0,
translateY: 0,
});
}
}
render() {
return (
<div>
<Samy path="/svg/sample.svg">
<SvgProxy
selector="#Image image"
transform={`translate(${this.state.translateX}, ${this.state.translateY})`}
/>
</Samy>
<button onClick={() => this.handleClick('up')}>Up</button>
<button onClick={() => this.handleClick('down')}>Down</button>
<button onClick={() => this.handleClick('right')}>Right</button>
<button onClick={() => this.handleClick('left')}>Left</button>
</div>
);
}
}
以下、コードの解説
Samy
とSvgProxy
をインポートする。
Samy
<Samy>
のpath
に読み込むSVGファイルのパスを記述。
SvgProxy
<SvgProxy>
は<Samy>
内に記述する。
上のコードのようにすることでSVGファイルの中からimage
タグを指定することができる。この機能が個人的にいい。
<SvgProxy selector="#Image image" />
あとは普通にSVGをカスタマイズするように、Reactのstateなりpropsなりを渡してあげればカスタマイズできる。今回の例ではボタンからの入力に応じて<image>
を位置調整したいので以下のようにした。
<SvgProxy
selector="#Image image"
transform={`translate(${this.state.translateX}, ${this.state.translateY})`}
/>
ちなみに、以下のようにすると<text>
の中身も変更できる。とても便利。
<SvgProxy selector="#hoge text">
{text}
</SvgProxy>
所感
以上のように、react-samy-svg
を利用すれば、手間いらずで結構自由度高くReactのコンポーネントとしてSVGをカスタマイズできると思います。
また、カスタマイズしたい箇所にはあらかじめIllustratorから特定のidを付けてもらうなどしてデザイナーと連携すれば効率が上がるのではないかなと思います。
補足
react-samy-svg
を使い始めて数日後、同じ作者によって新しくreact-svgmt
というツールが作成されていました。
今のところ機能はほぼ同じようですが、これからは後者を更新していくのかなって感じなので、react-svgmt
を使うのがいいかもです。
追記
また、やはりreact-svg-samy
には不具合があるようなので、react-svgmt
を使用することを強く勧めます。
不具合の例としては、<Samy path={path}>
のpath
が変更され再レンダリングされた際に<SvgProxy>
内が正しく更新されないなど。