日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

fabricjs 高级篇(自定义类型)

發(fā)布時間:2024/8/26 综合教程 27 生活家
生活随笔 收集整理的這篇文章主要介紹了 fabricjs 高级篇(自定义类型) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文:https://www.sitepoint.com/fabric-js-advanced/

<html>
<head>
     <script  src='./js/fabric.min.js'></script>
</head>
<body>
    <canvas id="c"></canvas>
<script>
var canvas = new fabric.Canvas('c',{backgroundColor : "#0ff", '600',height: '600'});
var LabeledRect = fabric.util.createClass(fabric.Rect, {
	    type: 'labeledRect',
	    initialize: function(options) {
		    options || (options = { });
		    this.callSuper('initialize', options);
		    this.set('label', options.label || '');
		    this.set('labelFont', options.labelFont || '');
		    this.set('labelFill', options.labelFill || '');
	    },
	    toObject: function() {
		    return fabric.util.object.extend(this.callSuper('toObject'), {
			    label: this.get('label'),
			    labelFont: this.get('labelFont'),
			    labelFill: this.get('labelFill')
		    });
	    },
	    _render: function(ctx) {
		    this.callSuper('_render', ctx);
		    // ctx.font = '20px Helvetica';
		    // ctx.fillStyle = '#333';
		    console.log('this', this);
		    ctx.font = this.labelFont;
		    ctx.fillStyle = this.labelFill;
		    // ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
		    ctx.fillText(this.label, 0, 0+10);
	    }
    });

    var labeledRect = new LabeledRect({
	     100,
	    height: 50,
	    left: 100,
	    top: 100,
	    label: 'test',
	    fill: '#faa',
        labelFont: '30px Helvetica',
        labelFill: '#00ff00'
    });
    canvas.add(labeledRect);
    setTimeout(function(){
	    labeledRect.set({
		    label: 'trololo',
		    fill: '#aaf',
		    rx: 10,
		    ry: 10,
            labelFill: '#0000ff'
	    });
	    canvas.renderAll();
    }, 3000)
</script>
</body>
</html>

  結(jié)果如下:

----------------------------------------

We’ve covered most of the basics of Fabric in thefirstandsecondparts of this series. In this article, I’ll move on to more advanced features: groups, serialization (and deserialization) and classes.

Groups

The first topic I’ll talk about is groups, one of Fabric’s most powerful features. Groups are exactly what they sound like—a simple way to group Fabric objects into a single entity so that you can work with those objects as a unit. (SeeFigure 1.)


Figure 1. A Selection Becomes a Group in Fabric

Remember that any number of Fabric objects on the canvas can be grouped with the mouse to form a single selection. Once grouped, the objects can be moved and even modified as one. You can scale the group, rotate it, and even change its presentational properties—its color, transparency, borders, and so on.

Every time you select objects like this on the canvas, Fabric creates a group implicitly, behind the scenes. Given this, it only makes sense to provide access to groups programmatically, which is wherefabric.Groupcomes in.

Let’s create a group from two objects, a circle and text:

       var text = new fabric.Text('hello world', {
	  fontSize: 30
	});
	var circle = new fabric.Circle({
	  radius: 100,
	  fill: '#eef',
	  scaleY: 0.5
	});
	var group = new fabric.Group([ text, circle ], {
	  left: 150,
	  top: 100,
	  angle: -10
	});
	canvas.add(group);

First, I created a “hello world” text object. Then, I created a circle with a 100 px radius, filled with the “#eef” color and squeezed vertically (scaleY=0.5). I next created afabric.Groupinstance, passing it an array with these two objects and giving it a position of 150/100 at a -10 degree angle. Finally, I added the group to the canvas, as I would with any other object, by usingcanvas.add().

Voila! You see an object on the canvas as shown inFigure 2, a labeled ellipse, and can now work with this object as a single entity. To modify that object, you simply change properties of the group, here giving it custom left, top and angle values.


Figure 2.A Group Created Programmatically

And now that we have a group on our canvas, let’s change it up a little:

       group.item(0).set({
	  text: 'trololo',
	  fill: 'white'
	});
	group.item(1).setFill('red');

Here, we access individual objects in a group via the item method and modify their properties. The first object is the text, and the second is the squeezed circle.Figure 3shows the results.


Figure 3. Squeezed Red Circle with New Text

One important idea you’ve probably noticed by now is that objects in a group are all positioned relative to the center of the group. When I changed the text property of the text object, it remained centered even when I changed its width. If you don’t want this behavior, you need to specify an object’s left/top coordinates, in which case they’ll be grouped according to those coordinates.

Here’s how to create and group three circles so that they’re positioned horizontally one after the other, such as those shown inFigure 4.

       var circle1 = new fabric.Circle({
	  radius: 50,
	  fill: 'red',
	  left: 0
	});
	var circle2 = new fabric.Circle({
	  radius: 50,
	  fill: 'green',
	  left: 100
	});
	var circle3 = new fabric.Circle({
	  radius: 50,
	  fill: 'blue',
	  left: 200
	});
	var group = new fabric.Group([ circle1, circle2, circle3 ], {
	  left: 200,
	  top: 100
	});
	canvas.add(group);


Figure 4. A Group with Three Circles Aligned Horizontally

Another point to keep in mind when working with groups is the state of the objects. For example, when forming a group with images, you need to be sure those images are fully loaded. Since Fabric already provides helper methods for ensuring that an image is loaded, this operation becomes fairly easy, as you can see in this code and in Figure 5.

       fabric.Image.fromURL('/assets/pug.jpg', function(img) {
	  var img1 = img.scale(0.1).set({ left: 100, top: 100 });
	  fabric.Image.fromURL('/assets/pug.jpg', function(img) {
	    var img2 = img.scale(0.1).set({ left: 175, top: 175 });
	    fabric.Image.fromURL('/assets/pug.jpg', function(img) {
	      var img3 = img.scale(0.1).set({ left: 250, top: 250 });
	      canvas.add(new fabric.Group([ img1, img2, img3],
	        { left: 200, top: 200 }))
	    });
	  });
	});


Figure 5. A Group with Three Images

Several other methods are available for working with groups:

getObjects works exactly like fabric.Canvas#getObjects() and returns an array of all objects in a group
size represents the number of objects in a group
contains allows you to check whether a particular object is in a group
item (which you saw earlier) allows you to retrieve a specific object from a group
forEachObject also mirrors fabric.Canvas#forEachObject, but in relation to group objects
add and remove add and remove objects from a group, respectively

You can add or remove objects with or without updating group dimensions and position. Here are several examples:

To add a rectangle at the center of a group (left=0, top=0), use this code:

       group.add(newfabric.Rect({
	...
	}));

To add a rectangle 100 px from the center of the group, do this:

       group.add(new fabric.Rect({
	  ...
	  left: 100,
	  top: 100
	}));

To add a rectangle at the center of a group and update the group’s dimensions, use the following code:

       group.addWithUpdate(newfabric.Rect({
	...
	left:group.getLeft(),
	top:group.getTop()
	}));

To add a rectangle at 100 px off from the center of a group and update the group’s dimensions, do this:

       group.addWithUpdate(newfabric.Rect({
	...
	left:group.getLeft()+100,
	top:group.getTop()+100
	}));

Finally, if you want to create a group with objects that are already present on the canvas, you need to clone them first:

       // create a group with copies of existing (2) objects
	var group = new fabric.Group([
	  canvas.item(0).clone(),
	  canvas.item(1).clone()
	]);
	// remove all objects and re-render
	canvas.clear().renderAll();
	// add group onto canvas
	canvas.add(group);

Serialization

As soon as you start building a stateful application of some sort—perhaps one that allows users to save results of canvas contents on a server or streaming contents to a different client—you need canvas serialization. There’s always an option to export the canvas to an image, but uploading a large image to a server requires a lot of bandwidth. Nothing beats text when it comes to size, and that’s exactly why Fabric provides an excellent support for canvas serialization and deserialization.

toObject, toJSON

The backbone of canvas serialization in Fabric are thefabric.Canvas#toObjectandfabric.Canvas#toJSONmethods. Let’s take a look at a simple example, first serializing an empty canvas:

       varcanvas=newfabric.Canvas('c');
	JSON.stringify(canvas);//'{"objects":[],"background":"rgba(0,0,0,0)"}'

Here I’m using the ES5JSON.stringifymethod, which implicitly calls the toJSON method on the passed object if that method exists. Because a canvas instance in Fabric has a toJSON method, it’s as though we calledJSON.stringify(canvas.toJSON())instead.

Notice the returned string that represents the empty canvas. It’s in JSON format, and essentially consists of “objects” and “background” properties. The “objects” property is currently empty because there’s nothing on the canvas, and “background” has a default transparent value (“rgba(0, 0, 0, 0)”).

Let’s give our canvas a different background and see how things change:

canvas.backgroundColor='red';
	JSON.stringify(canvas);//'{"objects":[],"background":"red"}'

As you would expect, the canvas representation reflects the new background color. Now let’s add some objects:

       canvas.add(new fabric.Rect({
	  left: 50,
	  top: 50,
	  height: 20,
	   20,
	  fill: 'green'
	}));
	console.log(JSON.stringify(canvas));

The logged output is as follows:

'{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,
"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,
"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,
"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}'

Wow! At first sight, quite a lot has changed, but looking more closely, you can see that the newly added object is now part of the “objects” array, serialized into JSON. Notice how its representation includes all its visual traits—left, top, width, height, fill, stroke and so on.

If we were to add another object—say, a red circle positioned next to the rectangle—you would see that the representation changed accordingly:

canvas.add(new fabric.Circle({
	  left: 100,
	  top: 100,
	  radius: 50,
	  fill: 'red'
	}));
	console.log(JSON.stringify(canvas));

Here’s the logged output now:

'{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,
"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,
"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,
"perPixelTargetFind":false,"rx":0,"ry":0},"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red",
"overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,
"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,
"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}'

Notice the “type”:”rect” and “type”:”circle” parts so that you can see better where those objects are. Even though it might seem like a lot of output at first, it is nothing compared to what you would get with image serialization. Just for fun, take a look at about one-tenth (!) of a string you would get withcanvas.toDataURL('png'):

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAK8CAYAAAAXo9vkAAAgAElEQVR4Xu3dP4xtBbnG4WPAQOQ2YBCLK1qpoQE1
/m+NVlCDwUACicRCEuysrOwkwcJgAglEItRQaWz9HxEaolSKtxCJ0FwMRIj32zqFcjm8e868s2fNWoJygl+e397rWetk5xf5pyZd13wPwIEC
BAgQIAAAQIECBxI4F0H+hwfQ4AAAQIECBAgQIAAgQsCxENAgAABAgQIECBAgMDBBATIwah9EAECBAgQIECAAAECAsQzQIAAAQIECBAgQIDAw
QQEyMGofRABAgQIECBAgAABAgLEM0CAAAECBAgQIECAwMEEBMjBqH0QAQIECBAgQIAAAQICxDNAgAABAgQIECBAgMDBBATIwah9EAECBAgQI
ECAAAECAsQzQIAAAQIECBAgQIDAwQQEyMGofRABAgQIECBAgAABAgLEM0CAAAECBAgQIECAwMEEBMjBqH0QAQIECBAgQIAAAQICxDNAgAABA
gQIECBAgMDBBATIwah9EAECBAgQIECAAAECAsQzQIAAAQIECBAgQIDAwQQEyMGofRABAgQIECBAgAABAgLEM0CAAAECBAgQIECAwMEEBMjBq
H0QAQIECBAgQIAAAQICxDNAgAABAgQIECBAgMDBBATIwah9EAECBAgQIECAAAECAsQzQIAAAQIECBAgQIDAwQQEyMGofRABAgQIECBAgAABA
gLEM0CAAAECBAgQIECAwMEEBMjBqH0QAQIECBAgQIAAAQICxDNAgAABAgQIECBAgMDBBATIwah9EAECBAgQIECAAAECAsQzQIAAAQIECBAgQ
IDAwQQEyMGofRABAgQIECBAgAABAgLEM0CAAAECBAgQIECAwMEEBMjBqH0QAQIECBAgQIAAAQICxDNAgAABAgQIECBAgMDBBATIwah9EAECB
AgQIECAAAECAsQzQIAAAQIECBAgQIDAwQQEyMGofRABAgQIECBAgAABAgLEM0CAAAECBAgQIECAwMEEBMjBqH0QAQIECBAgQIAAAQICxDNAg
AABAgQIECBAgMDBBATIwah9EAECBAgQIECAAAECyw+Qb134RU2fevC8q+5esGWESBAgAABAgQIEFiOwPLMC5AlvO0OBMCBAgQIECAAAECJxQ
QICcE9HYCBAgQIECAAAECBPYXECD7W3klAQIECBAgQIAAAQInFBAgJwT0dgIECBAgQIAAAQIE9hcQIPtbeSUBAgQIECBAgAABAicUECAnBPR
2AgQIECBAgAABAgT2FxAg+1t5JQECBAgQIECAAAECJxQQICcE9HYCBAgQIECAAAECBPYXECD7W3klAQIECBAgQIAAAQInFBAgJwTc9+3z49y
vmNd+dI7PzPHJOW6Y4wNzXD3HlXNc9pZdb85/vzbHK3P8aY7n5vj1HL+Y43dz417f97O9jgABAgQIECBAgMBSBATIKd2JCY5dWNwyx5fn+Pw
cV5U/6tXZ99M5fjjHk3Mjd6HifwQIECBAgAABAgQWLSBAirdnouP6WXfvHHfOcU1x9T6rXp4XPTLHA3NTX9jnDV5DgAABAgQIECBA4NACAuS
E4hMdl8+Kr83xzTmuO+G61ttfnEXfnuN7c4PfaC21hwABAgQIECBAgMBJBQTIJQpOeFw7b71/jtsvccWh3vbYfNB9c6NfOtQH+hwCBAgQIEC
AAAECFxMQIMd8No7C4+F5283HfOtZv/ypOYG7hMhZ3wafT4AAAQIECBDYtoAA2fP+H/1Vqwd3f4jf8y1Lfdkunu7xV7OWenucFwECBAgQIEB
g3QICZI/7O/Fxx7xs9wf3t36r3D3evciX7L7F7+6rIY8u8uycFAECBAgQIE

…and there’s approximately 17,000 characters more.

You might be wondering why there’s alsofabric.Canvas#toObject.Quite simply,toObjectreturns the same representation as toJSON, only in a form of the actual object, without string serialization. For example, using the earlier example of a canvas with just a green rectangle, the output forcanvas.toObjectis as follows:

       { "background" : "rgba(0, 0, 0, 0)",
	  "objects" : [
	    {
	      "angle" : 0,
	      "fill" : "green",
	      "flipX" : false,
	      "flipY" : false,
	      "hasBorders" : true,
	      "hasControls" : true,
	      "hasRotatingPoint" : false,
	      "height" : 20,
	      "left" : 50,
	      "opacity" : 1,
	      "overlayFill" : null,
	      "perPixelTargetFind" : false,
	      "scaleX" : 1,
	      "scaleY" : 1,
	      "selectable" : true,
	      "stroke" : null,
	      "strokeDashArray" : null,
	      "strokeWidth" : 1,
	      "top" : 50,
	      "transparentCorners" : true,
	      "type" : "rect",
	      "width" : 20
	    }
	  ]
	}

As you can see, toJSON output is essentially stringifiedtoObjectoutput. Now, the interesting (and useful) thing is thattoObjectoutput is smart and lazy. What you see inside an “objects” array is the result of iterating over all canvas objects and delegating to each object’s owntoObjectmethod. For example,fabric.Pathhas its owntoObjectthat knows to return path’s “points” array, andfabric.Imagehas atoObjectthat knows to return image’s “src” property. In true object-oriented fashion, all objects are capable of serializing themselves.

This means that when you create your own class, or simply need to customize an object’s serialized representation, all you need to do is work with thetoObjectmethod, either completely replacing it or extending it. Here’s an example:

var rect = new fabric.Rect();
	rect.toObject = function() {
	  return { name: 'trololo' };
	};
	canvas.add(rect);
	console.log(JSON.stringify(canvas));

The logged output is:

'{"objects":[{"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'

As you can see, the objects array now has a custom representation of our rectangle. This kind of override brings the point across but is probably not very useful. Instead, here’s how to extend a rectangle’stoObjectmethod with an additional property:

var rect = new fabric.Rect();
	rect.toObject = (function(toObject) {
	  return function() {
	    return fabric.util.object.extend(toObject.call(this), {
	      name: this.name
	    });
	  };
	})(rect.toObject);
	canvas.add(rect);
	rect.name = 'trololo';
	console.log(JSON.stringify(canvas));

And here’s the logged output:

'{"objects":[{"type":"rect","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)","overlayFill":null,
"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,
"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,
"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0,"name":"trololo"}],
"background":"rgba(0, 0, 0, 0)"}'

I extended the object’s existingtoObjectmethod with the additional property “name”, which means that property is now part of thetoObjectoutput, and as a result it appears in the canvas JSON representation. One other item worth mentioning is that if you extend objects like this, you’ll also want to be sure the object’s “class” (fabric.Rectin this case) has this property in the “stateProperties” array so that loading a canvas from a string representation will parse and add it to an object correctly.

toSVG

Another efficient text-based canvas representation is in SVG format. Since Fabric specializes in SVG parsing and rendering on canvas, it makes sense to make this a two-way process and provide canvas-to-SVG conversion. Let’s add the same rectangle to our canvas and see what kind of representation is returned from thetoSVGmethod:

       canvas.add(new fabric.Rect({
	  left: 50,
	  top: 50,
	  height: 20,
	   20,
	  fill: 'green'
	}));
	console.log(canvas.toSVG());

The logged output is as follows:

'<?xml version="1.0" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" 
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"><svg xmlns="http://www.w3.org/2000/svg" 
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="700" 
xml:space="preserve"><desc>Created with Fabric.js 0.9.21</desc><rect x="-10" y="-10" 
rx="0" ry="0" width="20" height="20" transform="translate(50 50)" /></svg>'

Just like withtoJSONandtoObject, thetoSVGmethod—when called on canvas—delegates its logic to each individual object, and each individual object has its owntoSVGmethod that is special to the type of object. If you ever need to modify or extend an SVG representation of an object, you can do the same thing withtoSVGas I did earlier withtoObject.

The benefit of SVG representation, compared to Fabric’s proprietarytoObject/toJSON, is that you can throw it into any SVG-capable renderer (browser, application, printer, camera, and so on), and it should just work. WithtoObject/toJSON, however, you first need to load it onto a canvas.

And speaking of loading things onto a canvas, now that you know how to serialize a canvas into an efficient chunk of text, how do you go about loading this data back onto canvas?

Deserialization and the SVG Parser

As with serialization, there’s two ways to load a canvas from a string: from JSON representation or from SVG. When using JSON representation, there are thefabric.Canvas#loadFromJSONandfabric.Canvas#loadFromDatalessJSONmethods. When using SVG, there arefabric.loadSVGFromURLandfabric.loadSVGFromString.

Notice that the first two methods are instance methods and are called on a canvas instance directly, whereas the other two methods are static methods and are called on the “fabric” object rather than on canvas.

There’s not much to say about most of these methods. They work exactly as you would expect them to. Let’s take as an example the previous JSON output from our canvas and load it on a clean canvas:

       varcanvas=newfabric.Canvas();
	canvas.loadFromJSON('{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20, 
fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,
"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,
"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,
"rx":0,"ry":0},"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red",
"overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,
"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,
"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,
"radius":50}],"background":"rgba(0,0,0,0)"}');

Both objects magically appear on canvas, as shown inFigure 6.


Figure 6. A Circle and a Square Rendered on Canvas

So loading canvas from a string is pretty easy, but what about that strange-lookingloadFromDatalessJSONmethod? How is it different fromloadFromJSON, which we just used? To understand why you need this method, look at a serialized canvas that has a more or less complex path object, like the one shown inFigure 7.


Figure 7. A Complex Shape Rendered on Canvas

JSON.stringify(canvas) output for the shape inFigure 7is as follows:

{"objects":[{"type":"path","left":184,"top":177,"width":175,"height":151,"fill":"#231F20","overlayFill":null,
"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,
"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,
"transparentCorners":true,"perPixelTargetFind":false,"path":[["M",39.502,61.823],["c",-1.235,-0.902,-3.038,
-3.605,-3.038,-3.605],["s",0.702,0.4,3.907,1.203],["c",3.205,0.8,7.444,-0.668,10.114,-1.97],["c",2.671,-1.302,
7.11,-1.436,9.448,-1.336],["c",2.336,0.101,4.707,0.602,4.373,2.036],["c",-0.334,1.437,-5.742,3.94,-5.742,3.94],
["s",0.4,0.334,1.236,0.334],["c",0.833,0,6.075,-1.403,6.542,-4.173],["s",-1.802,-8.377,-3.272,-9.013],["c",-1.468,
-0.633,-4.172,0,-4.172,0],["c",4.039,1.438,4.941,6.176,4.941,6.176],["c",-2.604,-1.504,-9.279,-1.234,-12.619,
0.501],["c",-3.337,1.736,-8.379,2.67,-10.083,2.503],["c",-1.701,-0.167,-3.571,-1.036,-3.571,-1.036],["c",1.837,
0.034,3.239,-2.669,3.239,-2.669],["s",-2.068,2.269,-5.542,0.434],["c",-3.47,-1.837,-1.704,-8.18,-1.704,-8.18],
["s",-2.937,5.909,-1,9.816],["C",34.496,60.688,39.502,61.823,39.502,61.823],["z"],["M",77.002,40.772],["c",0,0,
-1.78,-5.03,-2.804,-8.546],["l",-1.557,8.411],["l",1.646,1.602],["c",0,0,0,-0.622,-0.668,-1.691],["C",72.952,
39.48,76.513,40.371,77.002,40.772],["z"],["M",102.989,86.943],["M",102.396,86.424],["c",0.25,0.22,0.447,0.391,
0.594,0.519],["C",102.796,86.774,102.571,86.578,102.396,86.424],["z"],["M",169.407,119.374],["c",-0.09,-5.429,
-3.917,-3.914,-3.917,-2.402],["c",0,0,-11.396,1.603,-13.086,-6.677],["c",0,0,3.56,-5.43,1.69,-12.461],["c",
-0.575,-2.163,-1.691,-5.337,-3.637,-8.605],["c",11.104,2.121,21.701,-5.08,19.038,-15.519],["c",-3.34,-13.087,
-19.63,-9.481,-24.437,-9.349],["c",-4.809,0.135,-13.486,-2.002,-8.011,-11.618],["c",5.473,-9.613,18.024,-5.874,
18.024,-5.874],["c",-2.136,0.668,-4.674,4.807,-4.674,4.807],["c",9.748,-6.811,22.301,4.541,22.301,4.541],["c",
-3.097,-13.678,-23.153,-14.636,-30.041,-12.635],["c",-4.286,-0.377,-5.241,-3.391,-3.073,-6.637],["c",2.314,
-3.473,10.503,-13.976,10.503,-13.976],["s",-2.048,2.046,-6.231,4.005],["c",-4.184,1.96,-6.321,-2.227,-4.362,
-6.854],["c",1.96,-4.627,8.191,-16.559,8.191,-16.559],["c",-1.96,3.207,-24.571,31.247,-21.723,26.707],["c",
2.85,-4.541,5.253,-11.93,5.253,-11.93],["c",-2.849,6.943,-22.434,25.283,-30.713,34.274],["s",-5.786,19.583,
-4.005,21.987],["c",0.43,0.58,0.601,0.972,0.62,1.232],["c",-4.868,-3.052,-3.884,-13.936,-0.264,-19.66],["c",
3.829,-6.053,18.427,-20.207,18.427,-20.207],["v",-1.336],["c",0,0,0.444,-1.513,-0.089,-0.444],["c",-0.535,
1.068,-3.65,1.245,-3.384,-0.889],["c",0.268,-2.137,-0.356,-8.549,-0.356,-8.549],["s",-1.157,5.789,-2.758,
5.61],["c",-1.603,-0.179,-2.493,-2.672,-2.405,-5.432],["c",0.089,-2.758,-1.157,-9.702,-1.157,-9.702],["c",
-0.8,11.75,-8.277,8.011,-8.277,3.74],["c",0,-4.274,-4.541,-12.82,-4.541,-12.82],["s",2.403,14.421,-1.336,
14.421],["c",-3.737,0,-6.944,-5.074,-9.879,-9.882],["C",78.161,5.874,68.279,0,68.279,0],["c",13.428,16.088,
17.656,32.111,18.397,44.512],["c",-1.793,0.422,-2.908,2.224,-2.908,2.224],["c",0.356,-2.847,-0.624,-7.745,
-1.245,-9.882],["c",-0.624,-2.137,-1.159,-9.168,-1.159,-9.168],["c",0,2.67,-0.979,5.253,-2.048,9.079],["c",
-1.068,3.828,-0.801,6.054,-0.801,6.054],["c",-1.068,-2.227,-4.271,-2.137,-4.271,-2.137],["c",1.336,1.783,
0.177,2.493,0.177,2.493],["s",0,0,-1.424,-1.601],["c",-1.424,-1.603,-3.473,-0.981,-3.384,0.265],["c",0.089,
1.247,0,1.959,-2.849,1.959],["c",-2.846,0,-5.874,-3.47,-9.078,-3.116],["c",-3.206,0.356,-5.521,2.137,-5.698,
6.678],["c",-0.179,4.541,1.869,5.251,1.869,5.251],["c",-0.801,-0.443,-0.891,-1.067,-0.891,-3.473],...

…and that’s only 20 percent of the entire output!

What’s going on here? Well, it turns out that thisfabric.Pathinstance—this shape—consists of literally hundreds of Bezier lines dictating how exactly it is to be rendered. All those [“c”,0,2.67,-0.979,5.253,-2.048,9.079] chunks in JSON representation correspond to each one of those curves. And when there’s hundreds (or even thousands) of them, the canvas representation ends up being quite enormous.

Situations like these are wherefabric.Canvas#toDatalessJSONcomes in handy. Let’s try it:

       canvas.item(0).sourcePath='/assets/dragon.svg';
	console.log(JSON.stringify(canvas.toDatalessJSON()));

Here’s the logged output:

{"objects":[{"type":"path","left":143,"top":143,"width":175,"height":151,"fill":"#231F20","overlayFill":null,
"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,
"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,
"transparentCorners":true,"perPixelTargetFind":false,"path":"/assets/dragon.svg"}],"background":"rgba(0, 0, 0, 0)"}

That’s certainly smaller, so what happened? Notice that before callingtoDatalessJSON, I gave the path (dragon shape) object asourcePath property of “/assets/dragon.svg”. Then, when I calledtoDatalessJSON, the entire humongous path string from the previous output (those hundreds of path commands) is replaced with a single “dragon.svg” string.

When you’re working with lots of complex shapes,toDatalessJSONallows you to reduce canvas representation even further and replace huge path data representations with a simple link to SVG.

You can probably guess that theloadFromDatalessJSONmethod simply allows you to load a canvas from a data less version of a canvas representation. TheloadFromDatalessJSONmethod pretty much knows how to take those “path” strings (like “/assets/dragon.svg”), load them, and use them as the data for corresponding path objects.

Now, let’s take a look at SVG-loading methods. We can use either string or URL. Let’s look at the string example first:

       fabric.loadSVGFromString('...',function(objects,options){
	varobj=fabric.util.groupSVGElements(objects,options);
	canvas.add(obj).renderAll();
	});

The first argument is the SVG string, and the second is the callback function. The callback is invoked when SVG is parsed and loaded and receives two arguments—objects and options. The first, objects, contains an array of objects parsed from SVG—paths, path groups (for complex objects), images, text, and so on. To group those objects into a cohesive collection—and to make them look the way they do in an SVG document—we’re usingfabric.util.groupSVGElements and passing it both objects and options. In return, we get either an instance offabric.Pathorfabric.PathGroup, which we can then add onto our canvas.

Thefabric.loadSVGFromURLmethod works the same way, except that you pass a string containing a URL rather than SVG contents. Note that Fabric will attempt to fetch that URL via XMLHttpRequest, so the SVG needs to conform to the usual SOP rules.

Subclassing

Since Fabric is built in a truly object-oriented fashion, it’s designed to make subclassing and extension simple and natural. As described in the first article in this series, there’s an existing hierarchy of objects in Fabric. All two-dimensional objects (paths, images, text, and so on) inherit fromfabric.Object, and some “classes”—likefabric.PathGroup— even form a third-level inheritance.

So how do you go about subclassing one of the existing “classes” in Fabric, or maybe even creating a class of your own?

For this task you need thefabric.util.createClassutility method. This method is nothing but a simple abstraction over JavaScript’s prototypal inheritance. Let’s first create a simple Point “class”:

       var Point = fabric.util.createClass({
	  initialize: function(x, y) {
	    this.x = x || 0;
	    this.y = y || 0;
	  },
	  toString: function() {
	    return this.x + '/' + this.y;
	  }
	});

ThecreateClassmethod takes an object and uses that object’s properties to create a class with instance-level properties. The only specially treated property is initialize, which is used as a constructor. Now, when initializing Point, we’ll create an instance with x and y properties and thetoStringmethod:

       varpoint=newPoint(10,20);
	point.x;//10
	point.y;//20
	point.toString();//"10/20"

If we wanted to create a child of “Point” class—say a colored point—we would usecreateClasslike so:

       var ColoredPoint = fabric.util.createClass(Point, {
	  initialize: function(x, y, color) {
	    this.callSuper('initialize', x, y);
	    this.color = color || '#000';
	  },
	  toString: function() {
	    return this.callSuper('toString') + ' (color: ' + this.color + ')';
	  }
	});

Notice how the object with instance-level properties is now passed as a second argument. And the first argument receives Point “class”, which tellscreateClassto use it as a parent class of this one. To avoid duplication, we’re using thecallSupermethod, which calls the method of a parent class. This means that if we were to changePoint, the changes would also propagate to theColoredPointclass.

Here’s ColoredPoint in action:

var redPoint = new ColoredPoint(15, 33, '#f55');
	redPoint.x; // 15
	redPoint.y; // 33
	redPoint.color; // "#f55"
	redPoint.toString(); "15/35 (color: #f55)"

Now let’s see how to work with existing Fabric classes. For example, let’s create aLabeledRectclass that will essentially be a rectangle that has some kind of label associated with it. When rendered on our canvas, that label will be represented as a text inside a rectangle (similar to the earlier group example with a circle and text). As you’re working with Fabric, you’ll notice that combined abstractions like this can be achieved either by using groups or by using custom classes.

       var LabeledRect = fabric.util.createClass(fabric.Rect, {
	  type: 'labeledRect',
	  initialize: function(options) {
	    options || (options = { });
	    this.callSuper('initialize', options);
	    this.set('label', options.label || '');
	  },
	  toObject: function() {
	    return fabric.util.object.extend(this.callSuper('toObject'), {
	      label: this.get('label')
	    });
	  },
	  _render: function(ctx) {
	    this.callSuper('_render', ctx);
	    ctx.font = '20px Helvetica';
	    ctx.fillStyle = '#333';
	    ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
	  }
	});

It looks like there’s quite a lot going on here, but it’s actually pretty simple. First, we’re specifying the parent class asfabric.Rect, to utilize its rendering abilities. Next, we define the type property, setting it to “labeledRect“. This is just for consistency, because all Fabric objects have the type property (rect, circle, path, text, and so on.) Then there’s the already-familiar constructor (initialize), in which we utilizecallSuperonce again. Additionally, we set the object’s label to whichever value was passed via options. Finally, we’re left with two methods—toObjectand_render. ThetoObject method, as you already know from the serialization section, is responsible for object (and JSON) representation of an instance. SinceLabeledRecthas the same properties as regularrectbut also a label, we’re extending the parent’stoObjectmethod and simply adding a label into it. Last but not least, the_rendermethod is what’s responsible for the actually drawing of an instance. There’s anothercallSupercall in it, which is what renders rectangle, and an additional three lines of text-rendering logic.

If you were to render such object, you do something like the following.Figure 8shows the results.

       var labeledRect = new LabeledRect({
	   100,
	  height: 50,
	  left: 100,
	  top: 100,
	  label: 'test',
	  fill: '#faa'
	});
	canvas.add(labeledRect);


Figure 8. Rendering of labeledRect

Changing the label value or any of the other usual rectangle properties would obviously work as expected, as you can see here and inFigure 9.

labeledRect.set({
	  label: 'trololo',
	  fill: '#aaf',
	  rx: 10,
	  ry: 10
	}


Figure 9. Modified labeledRect

Of course, at this point you’re free to modify the behavior of this class anyway you want. For example, you could make certain values the default values to avoid passing them every time to the constructor, or you could make certain configurable properties available on the instance. If you do make additional properties configurable, you might want to account for them intoObjectandinitialize, as I’ve shown here:

       ...
	initialize: function(options) {
	  options || (options = { });
	  this.callSuper('initialize', options);
	  // give all labeled rectangles fixed width/height of 100/50
	  this.set({  100, height: 50 });
	  this.set('label', options.label || '');
	}
	...
	_render: function(ctx) {
	  // make font and fill values of labels configurable
	  ctx.font = this.labelFont;
	  ctx.fillStyle = this.labelFill;
	  ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
	}
	...

Wrapping Up

That closes the third installment of this series, in which I’ve dived into some of the more advanced aspects of Fabric. With help from groups, serialization and deserialization and classes, you can take your app to a whole new level.

總結(jié)

以上是生活随笔為你收集整理的fabricjs 高级篇(自定义类型)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 午夜免费播放观看在线视频 | 日本猛少妇色xxxxx猛叫 | 91精品视频在线看 | 韩日av一区二区 | 优优色影院 | 超碰caopor| 成人在线免费小视频 | 四虎8848精品成人免费网站 | 久久网站av | www.超碰在线观看 | 青青草日本 | 日韩欧美综合久久 | 亚洲AV午夜福利精品一级无码 | 国模精品一区二区三区 | 精品人妻一区二区三区香蕉 | 糖心av | 性生交大片免费看 | 欧美日韩性生活 | 激情网站视频 | 中文字幕亚洲乱码熟女一区二区 | 六月丁香综合 | 波多野结衣视频一区二区 | 亚洲国产成人一区二区 | 视频区小说区 | 91精品国产日韩91久久久久久 | 亚洲视频色图 | 精品久久久久久无码中文野结衣 | 日韩在线一 | 91亚色视频在线观看 | 日韩在线视频在线观看 | 一区二区三区在线看 | 一本大道久久a久久综合婷婷 | 久久艹在线视频 | 久久精品一二区 | 永久免费,视频 | 中文字幕激情 | 日韩欧美精品一区二区 | 欧美人体视频 | youjizz在线视频 | 亚州av免费 | 草莓视频18免费观看 | 91美女精品| 国产av第一区 | www.黄在线观看 | 蜜桃无码一区二区三区 | 日韩午夜激情 | 亚洲色图欧美另类 | 国产1区2区3区中文字幕 | 天天插天天狠 | 欧美成人精品欧美一 | 亚洲美女中文字幕 | 色多多在线看 | 亚洲av永久无码国产精品久久 | 伊人久久免费视频 | 福利姬在线播放 | 一边摸上面一边摸下面 | 一卡二卡三卡四卡 | 亚洲熟妇av一区二区三区 | 人妻少妇偷人精品久久性色 | 日韩经典一区二区三区 | 国产噜噜噜| 激情婷 | 日韩精品欧美精品 | 97久久久久久久久久 | 精品中文视频 | 男人天堂久久 | 欧美亚洲国产成人 | 欧美一区二区影院 | 国产精品久久久久久久久久久久久久久久久久 | 亚洲乱码日产精品bd在线观看 | 中文字幕91| 成年人国产精品 | 色哟哟一区二区三区四区 | 国产专区在线 | 亚洲理论电影在线观看 | 久久久久玖玖 | 久免费一级suv好看的国产 | 亚洲精品1区2区 | 精品一区二区三区四 | 94av视频| 色哟哟在线播放 | www.成人av.com| 四房婷婷 | 尤物视频网站在线观看 | 91精彩视频在线观看 | 国产无遮挡免费观看视频网站 | 欧美一级性生活视频 | 成人香蕉视频在线观看 | 久草视频在线免费播放 | 成年黄色片 | 国产伊人精品 | 国精产品一二三区精华液 | 日本国产亚洲 | 99久久久无码国产精品免费蜜柚 | 偷拍一区二区 | 精品福利一区二区三区 | 日韩 在线 | 日本毛片网站 | 亚洲精品在 |