web-dev-qa-db-ja.com

どのように私はFabric.jsを使用すると反応することができますか?

Fabric.js を使用して、HTML5キャンバスを多用するアプリケーションがあります。アプリは Angular 1.xの上に記述されており、 React に移行する予定です。私のアプリでは、テキストを書いたり、線、長方形、楕円を描くことができます。このようなオブジェクトの1つ以上を移動、拡大、縮小、選択、切り取り、コピー、貼り付けすることもできます。さまざまなショートカットを使用してキャンバスをズームとパンすることも可能です。要するに、私のアプリは、その完全な範囲までFabric.jsを利用しています。

Fabric.jsをReactと一緒に使用する方法についてはあまり情報を見つけることができなかったので、1。大幅な修正なしで可能です。リアクトのためのより良いサポートを持っていますか?

私が見つけることができたReact + Fabric.jsの唯一の例は、 react-komik でしたが、これは私のアプリよりもはるかに簡単です。私の主な関心事は、Fabric.jsのイベント処理とDOM操作、およびそれらのReactへの影響です。

react-canvas と呼ばれるReactのキャンバスライブラリもあるようですが、Fabric.jsに比べて多くの機能が欠けているようです。

ReactアプリでFabric.jsを使用する場合、DOM操作、イベント処理などに関して)何を考慮する必要がありますか?

40
Kitanotori

アプリで、React内でFabric.jsを使用する方法について同じ問題がありました。私の推奨事項は、ファブリックを管理されていないコンポーネントとして扱うことです。アプリ全体が通信できるファブリックインスタンスを作成し、ファブリック呼び出しを行います。その後、何か変更があった場合、.toObject()呼び出しを使用して、ファブリック全体の状態をReduxストアに入れます。次に、Reactアプリは、通常のReactアプリで行うように、グローバルRedux状態からファブリック状態を読み取ることができます。

StackOverflowコードエディターで動作するサンプルを取得することはできませんが、 ここにJSFiddleの例があります 推奨するパターンを実装しています。

36
StefanHayden

概念実証プロジェクトにFabricを使用しましたが、一般的な考え方は、たとえばD3と同じです。 FabricはDOM要素上で動作し、ReactはデータをDOMにレンダリングし、通常はDOMが延期されることに注意してください。

コンポーネントがマウントされるまで待ちます

それを行うには、Fabricインスタンス化をcomponentDidMountに配置します。

import React, { Component } from 'react';
import { fabric } from 'react-fabricjs';
import styles from './MyComponent.css';

class MyComponent extends Component {
  componentWillMount() {
    // dispatch some actions if you use Redux
  }

  componentDidMount() {
    const canvas = new fabric.Canvas('c');

    // do some stuff with it
  }

  render() {
    return (
      <div className={styles.myComponent}>
        <canvas id="c" />
      </div>
    )
  }
}

FabricコンストラクターをcomponentDidMountに配置すると、このメソッドが実行される時点でDOMの準備ができているため、失敗しないことが保証されます。 (ただし、Reduxを使用する場合に備えて、小道具は時々そうではありません)

参照を使用して実際の幅と高さを計算する

Refs は、実際のDOM要素への参照です。 DOM APIを使用してDOM要素でできることを参照で実行できます。子を選択し、親を見つけ、スタイルプロパティを割り当て、innerHeightおよびinnerWidthを計算します。後者はまさにあなたが必要とするものです:

componentDidMount() {
  const canvas = new fabric.Canvas('c', {
    width: this.refs.canvas.clientWidth,
    height: this.refs.canvas.clientHeight
  });

  // do some stuff with it
}

refsthisプロパティを定義することを忘れないでください。そのためには、コンストラクターが必要です。全体が次のようになります

import React, { Component } from 'react';
import { fabric } from 'react-fabricjs';
import styles from './MyComponent.css';

class MyComponent extends Component {
  constructor() {
    super()
    this.refs = {
      canvas: {}
    };
  }

  componentWillMount() {
    // dispatch some actions if you use Redux
  }

  componentDidMount() {
    const canvas = new fabric.Canvas('c', {
      width: this.refs.canvas.clientWidth,
      height: this.refs.canvas.clientHeight
    });

    // do some stuff with it
  }

  render() {
    return (
      <div className={styles.myComponent}>
        <canvas
          id="c"
          ref={node => {
            this.refs.canvas = node;
          } />
      </div>
    )
  }
}

ファブリックとコンポーネントの状態または小道具を混ぜる

Fabricインスタンスをコンポーネントの小道具や状態の更新に反応させることができます。動作させるには、componentDidUpdateでFabricインスタンスを更新するだけです(ご覧の通り、コンポーネントの独自のプロパティの一部として保存できます)。単純にrender関数呼び出しに依存しても、レンダリングされる要素はどれも新しい小道具や新しい状態で変更されないため、あまり役に立ちません。このようなもの:

import React, { Component } from 'react';
import { fabric } from 'react-fabricjs';
import styles from './MyComponent.css';

class MyComponent extends Component {
  constructor() {
    this.refs = {
      canvas: {}
    };
  }

  componentWillMount() {
    // dispatch some actions if you use Redux
  }

  componentDidMount() {
    const canvas = new fabric.Canvas('c', {
      width: this.refs.canvas.clientWidth,
      height: this.refs.canvas.clientHeight
    });

    this.fabric = canvas;

    // do some initial stuff with it
  }

  componentDidUpdate() {
    const {
      images = []
    } = this.props;
    const {
      fabric
    } = this;

    // do some stuff as new props or state have been received aka component did update
    images.map((image, index) => {
      fabric.Image.fromURL(image.url, {
        top: 0,
        left: index * 100 // place a new image left to right, every 100px
      });
    });
  }

  render() {
    return (
      <div className={styles.myComponent}>
        <canvas
          id="c"
          ref={node => {
            this.refs.canvas = node;
          } />
      </div>
    )
  }
}

画像のレンダリングを必要なコードに置き換えるだけで、新しいコンポーネントの状態またはプロップに依存します。新しいオブジェクトをレンダリングする前に、キャンバスをクリーンアップすることも忘れないでください!

17

react-fabricjs パッケージもあります。これにより、反応オブジェクトとしてファブリックオブジェクトを使用できます。実際、Rishatの答えにはこのパッケージが含まれていますが、react-fabricjsには「fabric」オブジェクトがないため、どのように動作するのかわかりません(おそらく「fabric-webpack」パッケージを意味します)。単純な「Hello world」コンポーネントの例:

import React from 'react';
import {Canvas, Text} from 'react-fabricjs';

const HelloFabric = React.createClass({
  render: function() {
    return (
      <Canvas
        width="900"
        height="900">
          <Text
            text="Hello World!"
            left={300}
            top={300}
            fill="#000000"
            fontFamily="Arial"
          />
      </Canvas>
    );
  }
});

export default HelloFabric;

このパッケージを使用したくない場合でも、コードを調べると、Fabric.jsをReactで自分で実装する方法を理解するのに役立ちます。

2

Fabric.jsには必要なすべての機能がありますが、Reactとの同期を維持するのに苦労した同様のユースケース用に react-shape-editor を作成しました/ Reduxコード。 Fabricほど多くの機能はありませんが、一部のユースケースの代替として適している場合があります。

0
Chris