先日紹介した、フリーハンドの線を描画できるライブラリ「react-signature-canvas」で、undo/redo 機能を実装したのでその方法についてまとめ。
「react-signature-canvas」ライブラリの GitHub のページはこちらから。
agilgur5/react-signature-canvas: A React wrapper component around signature_pad (in < 150 LoC). Unopinionated and heavily updated fork of react-signature-pad
https://github.com/agilgur5/react-signature-canvas
インストール方法については、以前投稿した「【React】手書きキャンバスを実装できるライブラリ「react-signature-canvas」」で紹介していますので、良ければこちらもご参照ください。
サンプルコードは下記のとおりです。
import React from 'react'
import SignatureCanvas from 'react-signature-canvas';
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
canvasHistory: [],
canvasHistoryPoint: 0,
};
this.saveCanvasHistory = () => {
const pointGroupArray = this.sigPad.toData();
this.setState({
canvasHistory: pointGroupArray,
canvasHistoryPoint: pointGroupArray.length,
});
}
this.undoCanvas = () => {
if (this.state.canvasHistoryPoint > 0) {
const array = this.state.canvasHistory.slice(0, this.state.canvasHistoryPoint - 1);
this.setState({ canvasHistoryPoint: array.length });
this.sigPad.fromData(array);
}
}
this.redoCanvas = () => {
if (this.state.canvasHistoryPoint < this.state.canvasHistory.length) {
const array = this.state.canvasHistory.slice(0, this.state.canvasHistoryPoint + 1);
this.setState({ canvasHistoryPoint: array.length });
this.sigPad.fromData(array);
}
}
}
render() {
return (
<div>
<Button onClick={this.undoCanvas}>元に戻す</Button>
<Button onClick={this.redoCanvas}>やり直し</Button>
<SignatureCanvas
penColor='green'
ref={(ref) => { this.sigPad = ref }}
canvasProps={{
width: 500, height: 200, className: 'sigCanvas'
}}
onEnd={this.saveCanvasHistory}
/>
</div>
);
}
}
export default Example;
今回のポイントは toData() と fromData() です。
toData() では、描画したフリーハンドの線を配列形式で取得することができます。
fromData() は、toData() で取得した配列形式のフリーハンドの線のデータを渡すことで、Canvas にそのデータの線を描画することができます。
これらを使って、描画した線のデータを操作します。
まず、フリーハンドの線の描画が終わったタイミングで毎回呼ばれる onEnd に指定した関数内で、描画した線の配列データを state に保存します。
const pointGroupArray = this.sigPad.toData();
this.setState({
canvasHistory: pointGroupArray,
canvasHistoryPoint: pointGroupArray.length,
});
この時、描画した線の数も保存します。
次に、「元に戻す」「やり直し」ボタンを設置し、それぞれをクリックした時に下記を実行するよう追加します。
// 元に戻す
if (this.state.canvasHistoryPoint > 0) {
const array = this.state.canvasHistory.slice(0, this.state.canvasHistoryPoint - 1);
this.setState({ canvasHistoryPoint: array.length });
this.sigPad.fromData(array);
}
// やり直し
if (this.state.canvasHistoryPoint < this.state.canvasHistory.length) {
const array = this.state.canvasHistory.slice(0, this.state.canvasHistoryPoint + 1);
this.setState({ canvasHistoryPoint: array.length });
this.sigPad.fromData(array);
}
なお、どちらの処理でも必要以上にクリックされたときにエラーが発生しないように条件式を追加していますが、可能なら同じ条件でボタン自体を押せなくするようにしておくといいと思います。
さて、行っている処理についてですが、まず「元に戻す」ボタンでは、クリックされるたびに state に保存していた線の描画配列データから一番最後のデータを取り除き、それを fromData() を使って Canvas に反映しています。
なお、この時 state のデータ自体を更新してしまうと、やり直し処理が働かなくなるので、描画する線の数データ canvasHistoryPoint を更新することで配列データの最後尾からデータを順に取り除いています。
「やり直し」ボタンではその逆の処理を行い、描画する線の数データ canvasHistoryPoint をインクリメントし、state に保存していた線の描画配列データを増やして取得・反映しているだけです。
上記コードで動作を確認したところ、私の環境では問題なく動作しました。
また、線の色についてもちゃんと保存されていました。
以上、React の「react-signature-canvas」ライブラリで undo/redo 機能を実装する方法についてでした。
ご参考になれば幸いです。