Fabric.js
Fabric.js の Doc. Wiki めも
Fabricを使うとき
Fabricが得意とすること
- オブジェクトの操作(オブジェクトは「単純な図形、画像、ベクトル図形、テキスト」のいずれか
- 操作は「移動、回転、サイズ変更」のいずれか
- 画像フィルタ(組み込みのフィルタは少なく、自分で定義するのも簡単)
- キャンバス上に多数のオブジェクトを表示する(パフォーマンス的に)
- リッチテキストを表示する(複数行、テキスト配置、テキスト装飾、背景、シャドウ、アウトライン)
- SVG を解析してキャンバス上に表示する SVG や、Fabric がサポートするコンテンツ(単純な図形、ベクトル図形、画像、テキスト)のいずれかを、Node.js 経由でサーバー上にレンダリングする。
- js 「単純なシェイプ、ベクターシェイプ、画像、テキスト」のアニメート
- 非透明コンテンツ(バウンディングボックス経由だけでなく)によるオブジェクトのグラブ
- 自由な描画(いくつかのブラシが内蔵されており、独自のブラシを定義するのも簡単です)
Fabricが苦手とすること
- 自明でない衝突検出(たとえば、ピクセル単位やカーソル単位での衝突検出。 スプライトベースのアニメーション(スプライトを抽象化することは可能ですが、ボックスの外には何もありません)
- 3Dオブジェクトの変換(three.jsのような3Dライブラリを使用するのがベストです。
Create a custom filter that swap colors
画像を処理するカスタムフィルターを作成する方法
Fabric.jsには新しいフィルターを作成するための基本ファイルがあります。今回はこのフィルターを「SwapColor」と呼ぶことに決めました。まず、この基本ファイルをコピーして、すべての「MyFilter」を「SwapColor」に置き換えます。
次に、本格的なコードを作成します。
このフィルターのアイデアは、画像内の色を取り、その色が完全に一致する場合に別の色と交換することです。これには、JavaScript(JS)部分とWebGL部分の両方でコードを書く必要があります。
前提条件
標準の1パラメータフィルターは「mainParameter」引数を使って自身を保存・復元できますが、このフィルターは2つのパラメータが必要です。したがって、「mainParameter」への参照を削除し、いくつかの情報を追加します。
フィルターはソースカラーと宛先カラーを処理する必要があるので、空の基本ファイルに以下の情報を追加します。
/**
* SwapColor colorSource、CSSカラー
* @param {String} colorSource
* @default
*/
colorSource: 'rgb(255, 0, 0)',
/**
* SwapColor colorDestination、CSSカラー
* @param {String} colorDestination
* @default
*/
colorDestination: 'rgb(0, 255, 0)',
さらに、フィルターが自身を保存し、復元できるようにします。
/**
* インスタンスのオブジェクト表現を返します
* @return {Object} インスタンスのオブジェクト表現
*/
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
colorSource: this.colorSource,
colorDestination: this.colorDestination,
});
}
次に、フィルターを2DCanvasとWebGLの両方で動作させる必要があります。
2DCanvasでのピクセル交換
フィルタークラス内で、applyTo2dが標準のWebGLサポートなしの関数です。このコードはブラウザで実行する場合はあまり必要ありませんが、Nodeでも使用する場合には便利です。ただし、Fabric.jsは両方のモードをサポートしているため、フィルターを書く場合は両方のモードに対応した方が良いです。
ロジックはシンプルです。各ピクセルについて、色が一致すれば宛先の色と交換します。色が一致するかどうかを判断するために、fabric.Colorを使ってCSSカラー文字列を解析し、最初の3つのコンポーネントを比較します。
/**
* SwapColor操作を画像のピクセルを表すUint8ClampedArrayに適用します。
*
* @param {Object} options
* @param {ImageData} options.imageData フィルタリングされるUint8ClampedArray。
*/
applyTo2d: function(options) {
var imageData = options.imageData,
data = imageData.data, i, len = data.length,
// fabric.Colorを使って、サポートされているカラー文字列からr,g,b値を取得します
source = new fabric.Color(this.colorSource).getSource(),
destination = new fabric.Color(this.colorDestination).getSource();
for (i = 0; i < len; i += 4) {
if (data[i] === source[0] && data[i + 1] === source[1] && data[i + 2] === source[2]) {
data[i] = destination[0];
data[i + 1] = destination[1];
data[i + 2] = destination[2];
}
}
},
これで、2DCanvasでの部分は完了です。
WebGL部分
WebGLに慣れていれば、この部分はとても簡単に見えるでしょうが、そうでない場合はいくつかの概念を理解する必要があります。ここでは、Fabric.jsがWebGLの基本機能を再利用可能なパーツにラップしているコードを紹介します。
WebGLシェーダーでの変数設定
まず、WebGLシェーダー内で変数を設定するためのコードを用意します。これには、getUniformLocationsとsendUniformDataという2つの関数が関わります。getUniformLocationsはシェーダー内での変数の呼び出し方を定義し、sendUniformDataはその変数にデータをバインドします。sendUniformDataを書くのはかなり難しい部分で、どの型の変数を使用するかを理解している必要があります。ここでは、色の文字列を0から1の範囲の4つの数値の配列に変換する必要があり、そのためにgl.uniform4fv関数を使用します。
/**
* このフィルターのシェーダー用WebGLユニフォームロケーションを返します。
*
* @param {WebGLRenderingContext} gl フィルターのシェーダーをコンパイルするために使用されるGLキャンバスコンテキスト。
* @param {WebGLShaderProgram} program このフィルターのコンパイル済みシェーダープログラム。
*/
getUniformLocations: function(gl, program) {
return {
uColorSource: gl.getUniformLocation(program, 'colorSource'),
uColorDestination: gl.getUniformLocation(program, 'colorDestination'),
};
},
/**
* このフィルターからシェーダープログラムのユニフォームにデータを送信します。
*
* @param {WebGLRenderingContext} gl フィルターのシェーダーをコンパイルするために使用されるGLキャンバスコンテキスト。
* @param {Object} uniformLocations ユニフォーム名からWebGLUniformLocationオブジェクトへのマップ
*/
sendUniformData: function(gl, uniformLocations) {
var source = new fabric.Color(this.colorSource).getSource(),
destination = new fabric.Color(this.colorDestination).getSource();
// WebGLでは色のコンポーネントは0から1の範囲でなければなりません
source[0] /= 255;
source[1] /= 255;
source[2] /= 255;
destination[0] /= 255;
destination[1] /= 255;
destination[2] /= 255;
gl.uniform4fv(uniformLocations.uColorSource, source);
gl.uniform4fv(uniformLocations.uColorDestination, destination);
},
WebGLシェーダーのソースコード
次に、WebGLシェーダーのソースコードが必要です。これはフィルターの処理を定義します。
/**
* SwapColorプログラムのフラグメントシェーダーソース
*/
fragmentSource: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform vec4 colorSource;\n' +
'uniform vec4 colorDestination;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = texture2D(uTexture, vTexCoord);\n' +
'vec3 delta = abs(colorSource.rgb - color.rgb);\n' +
'gl_FragColor = length(delta) < 0.02 ? colorDestination.rgba : color;\n' +
'}',
このコードでは、シェーダーが画像の色と指定された色との違いを計算し、その違いが小さい場合には指定された色に置き換えています。
実行してみよう
これで基本的なコードの説明は終了です。実際に動作を確認するためには、さらにスクロールして続きの情報を見てみてください。
Class: Image
Members | type | |
---|---|---|
lockMovementY | Boolean | true`の場合、オブジェクトの水平方向の動きはロックされる。 |
lockMovementY | Boolean | true`の場合、オブジェクトの垂直方向の動きはロックされる。 |
selectable | Boolean | false`に設定すると、オブジェクトを選択して変更することができなくなります(ポイント・クリック・ベースでもグループ・ベースでも選択可能です)。 しかし、イベントは発生します。 |
Class: Canvas
Head | Head | Head |
---|---|---|
Text | Text | Text |
Text | Text | Text |