fabric js how to use globalCompositeOperation in groups - fabricjs

I am trying to create groupe using fabricjs
var group = new fabric.Group(
[ circle, this.callerObject ],
{globalCompositeOperation:'xor'}
);
console.log(group);
this.mainCanvas.add(group);
But which globalCompositeOperation I have not set it is not working it always give the same result. I can make it using clear canvas, but I want to know, can I do it using native fabricjs methods?

I find solution myself)) to make it work need add globalCompositeOperation to the second object.
this.callerObject.set('globalCompositeOperation','xor');
var group = new fabric.Group(
[ circle, this.callerObject ]
);
But it have new problem)) this is work across all images)
To solve problem with cross showing
I have convert group to dataUrl, and to save state of object create new group, with object and image from previous group.
createXorGroup: function(object){
var self = this;
var baseStateTop = this.callerObject.top;
var baseStateLeft = this.callerObject.left;
this.callerObject.set('globalCompositeOperation','xor');
this.callerObject.set('active', false);
var group = new fabric.Group([ object, this.callerObject ]);
fabric.Image.fromURL(
group.toDataURL(),
function(image){
image.setOriginToCenter && image.setOriginToCenter();
self.callerObject.set('globalCompositeOperation','source-over');
self.callerObject.set('opacity', 0);
group = new fabric.Group([ self.callerObject, image ]);
self.mainCanvas.add(group);
group.setOriginToCenter && group.setOriginToCenter();
group.set('top', baseStateTop).set('left', baseStateLeft).setCoords();
group.setCenterToOrigin && group.setCenterToOrigin();
self.mainCanvas.remove(self.callerObject);
group.inCircle = true;
group.set('active', true);
},
{
originX: 'center',
originY: 'center',
top: this.callerObject.top,
left: this.callerObject.left
}
);
}
It is not native fabricjs object, I have override some properties for my work, but I hope you understand the main aim and it will be helpful
Continue to work with this library
To make xor to svg I have write this:
setGlobalCompositeOperation: function(object, type){
if(object.imageType == 'svg'){
for (var i = 0; i < object.paths.length; i++) {
this.setGlobalCompositeOperation(object.paths[i], type);
}
}else{
object.set('globalCompositeOperation', type);
}
}
But this do not work for text in mozila 31.6.0(( I'm looking solution for text

Related

Can't manage object alignments selection with FabricJs 2

I used to manage object alignments selection on FabricJS with getActiveGroup as below :
canvas.on("selection:created", function(e) {
var activeObj = canvas.getActiveObject();
console.log('e.target.type', e.target.type);
if(activeObj.type === "group") {
console.log("Group created");
var groupWidth = e.target.getWidth();
var groupHeight = e.target.getHeight();
e.target.forEachObject(function(obj) {
var itemWidth = obj.getBoundingRect().width;
var itemHeight = obj.getBoundingRect().height;
$('#objAlignLeft').click(function() {
obj.set({
left: -(groupWidth / 2),
originX: 'left'
});
obj.setCoords();
canvas.renderAll();
});
...
}
});
more detailed here
But now that I use FabricJS 2 and that getActiveObject() has been removed, I don't know what to do. I read on the doc that we could use getActiveObjects(), but it does nothing.
Please how can I reproduce the action of this code with FabricJS 2 (where getActiveGroup isn't supported anymore) ?
Selections of more than one object have the type activeSelection. The group type is only used when you purposefully group multiple objects using new fabric.Group([ obj1, obj2]
When you create a multi-selection using the shift-key as opposed to drawing a selection box, you'll trigger the selection:created event only on the first object selected, while objects added to the selection will trigger the selection:updated event. By calling your alignment code from both the selection:created and selection:updated events you'll make sure that your code is executed every time a multi-selection is created.
Also, you can use getScaledWidth() and getScaledHeight() to get scaled dimensions, or just .width and .height if you just want the unscaled width/height values. Good luck!
canvas.on({
'selection:updated': function() {
manageSelection();
},
'selection:created': function() {
manageSelection();
}
});
function manageSelection() {
var activeObj = canvas.getActiveObject();
console.log('activeObj.type', activeObj.type);
if (activeObj.type === "activeSelection") {
console.log("Group created");
var groupWidth = activeObj.getScaledWidth();
var groupHeight = activeObj.getScaledHeight();
activeObj.forEachObject(function(obj) {
var itemWidth = obj.getBoundingRect().width;
var itemHeight = obj.getBoundingRect().height;
console.log(itemWidth);
$('#objAlignLeft').click(function() {
obj.set({
left: -(groupWidth / 2),
originX: 'left'
});
obj.setCoords();
canvas.renderAll();
});
});
}
}

How to change z-index on sprite properly?

I have google it and I know that properly way to add z-index is by order of creating but I have different situation.
I'm creating card game and I want to move card on click and after that card should be on top which is not possible so I have solved in following way.
In function which is called on card click I'm destroying that card and creating identically new card which is on top because it is created last.
Is there another way of solving this situation?
Thank you
On card release you could use BringToTop (see also moveDown, moveUp and sendToBack methods that can be usefull to you)
Something like:
game.world.bringToTop(activeCard);
After this line, activeCard should be on top of all others sprites or groups.
if the objects/sprites are not in the same group game.world.bringToTop(target) will not work.
a more solid solution is to add objects to groups in the order you want them to be layered like so:
// Groups for drawing layers
var back_layer = game.add.group();
var mid_layer = game.add.group();
var front_layer = game.add.group();
// It doesn't matter what order you add things to these groups, the draw order will be back, mid, front (unless you change it...)
back_layer.create(0, 0, "bg");
front_layer.create(0, 0, "front");
mid_layer.create(300, 200, "object1");
mid_layer.create(500, 400, "object2");
explained here - www.html5gamedevs.com post
Personally I worked with both solutions and the groups one proved to be much more workable as the project starts to scale
hope this helps
You can test ordering with these;
var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update });
game.global = {};
function preload () {
}
function create () {
game.physics.startSystem(Phaser.Physics.ARCADE);
game.stage.backgroundColor = '#01555f';
this.gfx = game.add.graphics(0, 0);
this.gfx.visible = false;
this.gfx.beginFill(0x02C487, 1);
this.gfx.drawRect(100, 100, 250, 1);
this.barSprite = game.add.sprite(100, 500, this.gfx.generateTexture());
this.barSprite.anchor.y = 1;
game.add.tween(this.barSprite).to({ height: 400 }, 1000, Phaser.Easing.Default, true, 0);
var bubbleGfx = game.add.graphics(0, 0);
bubbleGfx.lineStyle(2 * 1, 0x000000, 1);
bubbleGfx.visible = true;
bubbleGfx.beginFill(0xffffff, 1);
bubbleGfx.drawRoundedRect(300, 150, 200, 100, 8);
this.gfx2 = game.add.graphics(0, 0);
this.gfx2.visible = false;
this.gfx2.beginFill(0x02C111, 1);
this.gfx2.drawRect(150, 100, 250, 1);
this.barSprite2 = game.add.sprite(400, 500, this.gfx2.generateTexture());
this.barSprite2.anchor.y = 1;
game.add.tween(this.barSprite2).to({ height: 400 }, 1000, Phaser.Easing.Default, true, 0);
}
function update () {
}

fabric.js canvas to json with svg custom attributes

I added new attributes for path svg:
var canvas = new fabric.Canvas('c');
var group = [];
fabric.loadSVGFromURL(svg_file, function(objects,options) {
var obj = fabric.util.groupSVGElements(objects, options);
obj.set({
left: 100,
top: 100
});
canvas.add(obj);
canvas.renderAll();
},
// add new attributes
function(item, object) {
object.set('id',item.getAttribute('id'));
object.set('tag_id', tag_id);
object.set('elem_id', elem_id );
group.push(object);
}
);
And:
// see new attributes
console.log(canvas.getObjects());
// save to JSON
console.log('json:', canvas.toJSON())
But new attributes not save.
Saving JSON in canvas with fabric.js not work for me.
I read Saving JSON in canvas with fabric.js,
it says to create a new class based on fabric.Image, it works for me,
but what to do with fabric.LoadSVGFromURL and fabric.util.groupSVGElements?
Need to create a new class or something else? Help me.
Solved:
https://github.com/kangax/fabric.js/issues/471
http://jsfiddle.net/Ypn2k/4/
You should try to set custom attribute in this way :
svgelement.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
"custom_attr_key": "Value",
"custom_attr_key": "Value",
});
};
})(svgelement.toObject);
and also pass this custom attribute key in toJson function like This :
canvas.toJSON(['custom_attr_key','custom_attr_key']);

How to update a feature in an Openlayers.Layer?

i have an xml file with coordinates in it and i want to draw a line of those points on an Openlayers Map. I already have a Openlayers.Layer.Vector and i am creating a feature as follows:
var points = [];
for (var i = 0; i < coords.length; i++)
{
point = new OpenLayers.Geometry.Point(aPointsArray[i].lon, aPointsArray[i].lat);
points.push(point);
}
var geometry = new OpenLayers.Geometry.LineString(points);
var feature = new OpenLayers.Feature.Vector(geometry, null,
{
strokeColor: aColor,
strokeOpacity: 0.7,
strokeWidth: 3
});
aLayer.addFeatures([feature]);
This works a expected and i am seeing a line on my map. The problem is now, that the points i get from the .xml are changed dynamically by another program and I want to draw those changes live on my map. I already have a method that updates the maps periodically but how do i update the feature/geometry to the new points ?
Try aLayer.drawFeature(yourChangedFeature);

Is it possible to animate filter in Fabric.js?

Is it possible to animate the images filter in Fabric.js? Such as a "pixelate" filter.
I solved it in the same way like the demo.
Unfortunately filters aren't able to be animated - they need too much processing time.
Here's my Code:
image = ... //Image, where the filter should be applied
var filter = new fabric.Image.filters.RemoveWhite({
threshold: 0,
distance: 140
});
image.filters.push(filter);
image.applyFilters(canvas.renderAll.bind(canvas));
animate(image,1, 400); //Start the Animation
function animate(image,value, stop){
value += ((stop-value)*0.02); //Change the threshold-value
if (image.filters[0]) {
image.filters[0]['threshold'] = value;
console.log(value);
image.applyFilters(canvas.renderAll.bind(canvas)); //Start creating the new image
if(value<stop-100){
setTimeout(function(){act(image,value,stop);},1);
}
}
}
I know the code isn't the most efficient one, but it works. And you can see that Animating filters consumes too much time.
(I tested it with a 1920x1080px image, maybe you can use it with smaller images)
Here is a updated version for the brightness filter
var brightnessValue = 0.9;
var brightnessFilter = new fabric.Image.filters.Brightness({
brightness: brightnessValue
});
fabricImage.filters.push(brightnessFilter);
fabric.util.requestAnimFrame(function brightnessFilterAnimation() {
brightnessValue = brightnessValue - 0.04;
brightnessFilter.brightness = brightnessValue;
fabricImage.applyFilters();
if (brightnessValue > 0) {
fabric.util.requestAnimFrame(brightnessFilterAnimation);
}
});

Resources