Get z-index of selected object in fabric js - javascript

I am trying to get the z-index of selected object in fabric js. Is there a way to get that?
var z_index = 1;
$('#manage_index').change(function(){
var cur_value = $(this).val();
if(cur_value!='')
{
var object = canvas.getActiveObject();
if(cur_value=='up') // Means increase z-index
{
canvas.moveTo(object, z_index);
z_index = z_index + 1;
}
else if(cur_value=='back') //Means decrease z-index
{
//var temp_index = 0; // If set it to 0, it will goes into backward,
//But i am trying to implement something like below
var temp_index = canvas.get('z-index');// Get the z-index of selected object and then decrease it
canvas.moveTo(object, temp_index-1);
}
}
$(this).val('');
});

var canvas = new fabric.Canvas('a');
canvas.add(new fabric.Rect({
left:50,
top:50,
height:50,
width:50,
fill:'red'
}));
canvas.add(new fabric.Rect({
left:70,
top:70,
height:50,
width:50,
fill:'green'
}));
canvas.add(new fabric.Rect({
left:90,
top:90,
height:50,
width:50,
fill:'blue'
}));
canvas.renderAll();
function getIndex(){
var activeObj = canvas.getActiveObject();
console.log(activeObj && canvas.getObjects().indexOf(activeObj));
}
function bringToFront(){
var activeObj = canvas.getActiveObject();
activeObj && canvas.bringToFront(activeObj).discardActiveObject(activeObj).renderAll();
}
canvas {
border: 2px solid black;
}
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<button onclick='getIndex()'>Index</button>
<button onclick='bringToFront()'>bringToFront</button>
<canvas id="a" width="200" height="200"></canvas>
As fabric object stores in an array , you can get index using indexOf. to rearrange order you can use the bringToFront, bringForward, sendBackwards, sendToBack. And moveTo with to specified index.

I thought it was already implemented in recent Fabric.js versions but you can still extend Fabric objects prototype :
fabric.Object.prototype.getZIndex = function() {
return this.canvas.getObjects().indexOf(this);
}

Related

Alert not triggered when images touch each other

I am non-native English speaker and I am beginner of programming language. I understand that my explanation is not best but I am trying to explain better to people to understand what I am trying to do. So, please be patient with me and please not try to down vote (it hurt my feelings) instead of tell me why my explanation is bad. I appreciate your time to read this. Thank you.
I am working on canvas game called coin sorting game which is drag the coins to the correct piggy bank images. I am stuck with if condition right now. In the current state, alert will not trigger when specific image touch to other specific image. For example, when 1yen coin image touches with 1yen piggy bank image then trigger alert otherwise no event occur.
I thought adding images to if condition will set specific images but it did not work.
if (haveIntersection(obj.getClientRect(), targetRect)&& (ichiYenImg === ichiYenpiggyImg)) {
alert("Intersection");
}
Can anyone give me an advice how to attempt this problem?
Any help is greatly appreciated!
var stage = new Konva.Stage({
width: 400,
height: 200,
container: 'container'
});
var layer = new Konva.Layer();
stage.add(layer);
layer.on('dragmove', function(e) {
var target = e.target;
var targetRect = e.target.getClientRect();
layer.children.each(function(obj) {
if (obj === target) {
return;
}
if (haveIntersection(obj.getClientRect(), targetRect)&& (ichiYenImg === ichiYenpiggyImg)) {
alert("Intersection");
}
});
});
function haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width/2 ||
r2.x + r2.width/2 < r1.x ||
r2.y > r1.y + r1.height/2 ||
r2.y + r2.height/2 < r1.y
);
}
// This will draw the image on the canvas.
function drawImage(source, konvaImage) {
layer.add(konvaImage);
var image = new Image();
image.src = source;
image.onload = function() {
konvaImage.image(image);
layer.draw();
}
}
//1yen
var ichiYenImg = new Konva.Image({
x: 20,
y: 20,
width: 100,
height: 100,
draggable: true
});
var sourceImg1 = "https://illustrain.com/img/work/2016/illustrain09-okane5.png";
drawImage(sourceImg1, ichiYenImg);
var goYenImg = new Konva.Image({
x: 120,
y: 20,
width: 100,
height: 100,
draggable: true
});
var sourceImg2 = "https://illustrain.com/img/work/2016/illustrain09-okane7.png";
drawImage(sourceImg2, goYenImg);
//piggy bank 1yen
var ichiYenpiggyImg = new Konva.Image({
x: 300,
y: 100,
width: 100,
height: 100,
draggable: false
});
var sourceImg7 = "https://user-images.githubusercontent.com/31402838/63416628-a322b080-c3b4-11e9-96e8-e709ace70ec1.png";
drawImage(sourceImg7, ichiYenpiggyImg);
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/konva#4.0.5/konva.min.js"></script>
</head>
<body>
<div id="stage-parent">
<div id="container"></div>
</div>
</body>
</html>
Your code is mainly good - the issue is with the comparison of the objects in the 'if' statement copied below, where your intention is to decide if the user is dragging the correct value coin to the piggy bank. Comparing JavaScript objects is valid, but it can lead to unpredictable results - a more reliable & robust approach is to be more explicit, set a known attribute value on each object and compare those values.
if (haveIntersection(obj.getClientRect(), targetRect)&& (ichiYenImg === ichiYenpiggyImg)) {
alert("Intersection");
}
My answer in the code snippet is to modify the code to use the Konva 'name' variable to hold the coin and bank values. You can see I set them both to 1yen. Now in the dragMove() function I get the name attr from each of the objects being compared. When they match, we have a valid hit, and when no match the coin & bank combination are invalid.
I modified the code to put a red border around the bank when the correct coin is dragged.
See the Konva documentation for the name attr here.
var stage = new Konva.Stage({
width: 400,
height: 200,
container: 'container'
});
var layer = new Konva.Layer();
stage.add(layer);
layer.on('dragmove', function(e) {
var target = e.target;
var targetRect = e.target.getClientRect();
layer.children.each(function(obj) {
if (obj === target) {
return;
}
// capture the result of the intersection test.
var checkHit = haveIntersection(obj.getClientRect(), targetRect);
// get the objects name attribute
var nameDragged = e.target.attrs['name'];
var namePiggy = obj.attrs['name'];
// decide if they match
var checkNames = (nameDragged === namePiggy);
// finally decide if we have a valid hit
if (checkHit && checkNames) {
// hit ok !
obj.stroke('red');
obj.strokeWidth(2)
}
else {
// no hit or not matching name
obj.stroke(false);
obj.strokeWidth(0)
}
});
});
function haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width/2 ||
r2.x + r2.width/2 < r1.x ||
r2.y > r1.y + r1.height/2 ||
r2.y + r2.height/2 < r1.y
);
}
// This will draw the image on the canvas.
function drawImage(source, konvaImage) {
layer.add(konvaImage);
var image = new Image();
image.src = source;
image.onload = function() {
konvaImage.image(image);
layer.draw();
}
}
//1yen
var ichiYenImg = new Konva.Image({
x: 20,
y: 20,
width: 100,
height: 100,
draggable: true,
name: '1yen' // use the name attribute to indicate the coin value
});
var sourceImg1 = "https://illustrain.com/img/work/2016/illustrain09-okane5.png";
drawImage(sourceImg1, ichiYenImg);
var goYenImg = new Konva.Image({
x: 120,
y: 20,
width: 100,
height: 100,
draggable: true
});
var sourceImg2 = "https://illustrain.com/img/work/2016/illustrain09-okane7.png";
drawImage(sourceImg2, goYenImg);
//piggy bank 1yen
var ichiYenpiggyImg = new Konva.Image({
x: 300,
y: 100,
width: 100,
height: 100,
draggable: false,
name: '1yen' // use the name attribute to indicate the coin value
});
var sourceImg7 = "https://user-images.githubusercontent.com/31402838/63416628-a322b080-c3b4-11e9-96e8-e709ace70ec1.png";
drawImage(sourceImg7, ichiYenpiggyImg);
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/konva#4.0.5/konva.min.js"></script>
</head>
<body>
<div id="stage-parent">
<div id="container"></div>
</div>
</body>
</html>

How to access the index position i in the drag stop handler of snapsvg

I'm grouping a few elements using snapSVG's group method, pushing them to an array and applying the drag method on the array elements by looping through each element.
Could you please help me in accessing the index postion of the dragged element (grps[i]) in the drag stop handler.
g1 and var g2 are the two gropus.
grps is the array that holds the two groups.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
</head>
JavaScript
var s = Snap(800, 600);
var grps = [];
var objects = [];
var red = s.rect(50, 50, 200, 200).attr({
fill: "red"
});
var green = s.rect(60, 60, 100, 100).attr({
fill: "green"
});
var g1 = s.group(red, green);
grps.push(g1);
var red = s.rect(300, 50, 200, 200).attr({
fill: "red"
});
var green = s.rect(310, 60, 100, 100).attr({
fill: "green"
});
var g2 = s.group(red, green);
grps.push(g1, g2);
var drag_move = function(dx, dy) {
this.attr({
transform: this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]
});
};
var drag_start = function() {
this.data('origTransform', this.transform().local);
};
var drag_stop = function(i) {
console.log("finished dragging");
console.log(i);
};
for (i = 0; i < grps.length; i++) {
grps[i].drag(drag_move, drag_start, drag_stop);
}
JsBin Link: http://jsbin.com/tonazosicu/10/edit?js
Thanks
You can using Function.prototype.bind() to preset some parameters like below
for (i = 0; i < grps.length; i++) {
grps[i].drag(drag_move, drag_start, drag_stop.bind(null, i));
}
Then on drag_stop you can access them like below.
var drag_stop = function(index, event) {
console.log("finished dragging");
console.log(index);
console.log(event);
};
One can achieve the same thing (in lastest versions of Snap I think) with...
grps.ForEach( function( el, i ) {
el.drag(drag_move, drag_start, drag_stop.bind(null, i))
};
But ultimately you don't need to use i, if you just use 'this' in the handler in most cases, and can simply do....
grps.ForEach( function( el ) {
el.drag(drag_move, drag_start, drag_stop)
};

Select all text objects on a Fabric.js canvas

var canvas = new fabric.Canvas();
// select all objects
function selectAllCanvasObjects(){
var objs = canvas.getObjects().map(function(o) {
return o.set('active', true);
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
}
I have a canvas and I need to select all text objects and skip others. This is the code to select all objects, how can I make it only select all text objects and skip others?
The following example only selects items with the type of 'text'.
In summary:
The Fabric JS get method allows us to inspect the type of the current item that we're iterating over
If the type is equal to 'text' then we return the item
N.B. We now use filter instead of map, as we now only want to return items that match the type of 'text', instead of every item
var canvas = new fabric.Canvas('c');
// Add some example shapes
var circle = new fabric.Circle({
radius: 20, fill: 'green', left: 100, top: 100
});
var triangle = new fabric.Triangle({
width: 20, height: 30, fill: 'blue', left: 50, top: 50
});
canvas.add(circle, triangle);
// Add some example text
var text1 = new fabric.Text('hello world', { left: 100, top: 100 });
var text2 = new fabric.Text('test', { left: 0, top: 0 });
canvas.add(text1, text2);
// Select all objects
function selectAllCanvasObjects(){
var objs = canvas.getObjects().filter(function(o) {
if (o.get('type') === 'text') {
return o.set('active', true);
}
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
}
selectAllCanvasObjects();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.8/fabric.min.js"></script>
<canvas id="c"></canvas>
You can use below code to select all text object.
var object_length = parseInt(canvas.getObjects().length) - 1;
for(var i = 0; i <= object_length; i++)
{
canvas.setActiveObject(canvas.item(i));
var obj = canvas.getActiveObject();
var object_type = obj.type;
if(object_type == "text")
{
//Write your code here
canvas.renderAll();
}
}
canvas.deactivateAllWithDispatch();
canvas.renderAll();
After completion of execution deselect all objects so it will not show you last selected object as selected.

Kineticjs Object Handling & Event Listener

I sell custom equipment and on my site, I have a flash tool where customers can assign colors to glove parts and see what it will look like.
I've been working on a HTML5 version of this tool, so the iPad crowd can do the same thing. Click here for what I've done,
I took kineticjs multiple picture loader and hacked it to load all the pics necessary to stage and the color buttons, which are multiple instances of the same image. In their example, it was only 2 images, so they var name each image, which were manipulative. My goal is to dynamically create variable, based on image name.
I'm using a for loop and if statements to position the parts according to their type. If the image being loaded is a button, the original instance is not added to the stage, but another for loop, with a counter, creates instances and put on the stage. The variable is part string+n (wht0). From here I initiate an eventlistener, when clicked is suppose to hide all glove parts pertaining to the option and show the appropriate color. That code I have already in my AS.
I created an eventlistener on the white buttons (first button) that when clicked, I set it to hide one of the white leather part of glove. But when I click the button, I get the error in console that the glove part (ex wlt_wht), I get an error stating that the object is not defined. But when the image was loaded the variable name came from the current array object being loaded.
I added another variable before the callback call, to convert the content of the array to a string and used the document.write to confirm that the object name is correct, but after creating the object its now [object object]. In flash, you manually assign the movie clip name and target.name is available if you call it.
How can I write the Image obj so I can control the object? In the doc there is a reference for id and name as properties of the object, but when I set these, it did not work with me. Sure, I could have manually created each Kinetic.Image(), but there's no fun in that.. especially with 191 images. Any tip on how I can get around this problem?
Checkout http://jsfiddle.net/jacobsultd/b2BwU/6/ to examine and test script.
function loadImages(sources, callback) {
var assetDir = 'http://dev.nystixs.com/test/inf/';
var fileExt = '.png';
var images = {};
var loadedImages = 0;
var numImages = 0;
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function () {
var db = sources[src].toString();
var dbname = db.slice(-0, -4);
if (++loadedImages >= numImages) {
callback(images, dbname);
}
};
images[src].src = assetDir + sources[src];
//images[src].src = assetDir+sources[src]+".png";
}
}
function initStage(images, db) {
var shapesLayer = new Kinetic.Layer();
var messageLayer = new Kinetic.Layer();
//Loading Images
var xpos = 0;
var ypos = 200;
for (var i in images) {
var glvP = i.slice(0, 3);
db = new Kinetic.Image({
image: images[i],
x: xpos,
y: ypos
});
if (glvP == "wlt") {
shapesLayer.add(db);
db.setPosition(186.95, 7.00);
//db.hide();
shapesLayer.draw();
} else if (glvP == "lin") {
shapesLayer.add(db);
db.setPosition(204.95, 205.00);
} else if (glvP == "plm") {
shapesLayer.add(db);
db.setPosition(311.95, 6.00);
} else if (glvP == "web") {
shapesLayer.add(db);
db.setPosition(315.95, 7.00);
} else if (glvP == "lce") {
shapesLayer.add(db);
db.setPosition(162.95, 3.00);
} else if (glvP == "thb") {
shapesLayer.add(db);
db.setPosition(63.00, 28.60);
} else if (glvP == "bfg") {
shapesLayer.add(db);
db.setPosition(167.95, 7.00);
} else if (glvP == "wst") {
shapesLayer.add(db);
db.setPosition(208.95, 234.00);
} else if (glvP == "fpd") {
shapesLayer.add(db);
db.setPosition(252.95, 82.00);
} else if (glvP == "bac") {
shapesLayer.add(db);
db.setPosition(0, 0);
} else if (glvP == "bnd") {
shapesLayer.add(db);
db.setPosition(196.95, 164.00);
} else {}
var rect = new Kinetic.Rect({
x: 710,
y: 6,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 200,
height: 325,
shadowColor: 'white',
shadowBlur: 10,
shadowOffset: [5, 5],
shadowOpacity: 0.2,
cornerRadius: 10
});
shapesLayer.add(rect);
// End of Glove Parts Tabs
//Load Color Buttons
if (glvP == "wht") {
xpos = -5.00;
bpos = 375;
var zpos = -5.00;
var tpos = -5.00;
db.setPosition(xpos, bpos);
//shapesLayer.add(db);
var n = 0;
for (n = 0; n < 12; n++) {
if (n < 4) {
var glvB = "wht" + n;
var btn = glvB;
glvB = new Kinetic.Image({
image: images[i],
width: 18,
height: 18,
id: 'wht0'
});
glvB.on('mouseout', function () {
blankText('');
});
glvB.on('mouseover', function () {
writeColors('White', btn);
});
glvB.on('click', function () {
console.log(glvB + " clicked");
wht.hide();
shapesLayer.draw();
});
glvB.setPosition((xpos + 20), bpos);
shapesLayer.add(glvB);
xpos = (xpos + 230);
}
You can use your .png image filenames to automate your color-button coding efforts.
No need to manually code 10 glove components X 21 colors per component (210 color buttons).
Assume you’ve split the each image URL (filename) to get the color and glove-type.
Then you can create all 210 color buttons with one piece of reusable code.
Demo: http://jsfiddle.net/m1erickson/H5FDc/
Example Code:
// Usage:
addColorButton(100,100,"red","fingers");
// 1 function to add 210 color-buttons
function addColorButton(x,y,color,component){
// create this button
var button=new Kinetic.Image({
x:x,
y:y,
image: imageArray[ color+"-color-button" ],
});
// save the color as a property on this button
button.gloveColor=color;
// save the glove component name as a property on this button
button.gloveComponent=component; // eg, "fingers"
// resuable click handler
// Will change the gloves "#fingers" to "red-fingers-image"
button.on("click",function(){
// eg. get the image "red-fingers-image"
var newImage = imageArray[this.gloveColor+"-"+this.gloveComponent+"-image"];
// eg. get the Kinetic.Image with id:”finger”
var glovePart = layer.find("#"+this.gloveComponent”][0];
// change the Kinetic id:finger’s image
// to the red-fingers-image
glovePart.setImage(newImage);
layer.draw();
});
layer.add(button);
}

How to best debug a layer which is not visible

I'm having a Kinetic.Layer which I don't understand why it doesn't show. And I wonder if there is a (easy) way to debug or see why it won't show.
If I use a Kinetic.Group it is shown, so I'm troubled what's the difference between those to and how to solve it.
Strange thing is that just changing from a Group to a Layer with the exact same code make it disappears.
Here is the code:
createTickerGroup: function (index) {
var symbolGroup = new Kinetic.Layer({
opacity: 0.8,
clearBeforeDraw: true
});
var fontSize = Math.floor(this.layer.getHeight() * 0.8);
var textConfig = {
text: "text " + index,
align: "left",
fontSize: fontSize,
fontFamily: "TSTARMedium",
textFill: "#000000",
clearBeforeDraw: true
};
var symbolText = new Kinetic.Text(textConfig);
symbolText.setName("#nm");
var pcText = new Kinetic.Text(textConfig);
pcText.setName("#pc");
var chText = new Kinetic.Text(textConfig);
chText.setName("#ch");
var chpText = new Kinetic.Text(textConfig);
chpText.setName("#chp");
var crText = new Kinetic.Text(textConfig);
crText.setName("#cr");
symbolGroup.add(symbolText);
symbolGroup.add(pcText);
symbolGroup.add(chText);
symbolGroup.add(chpText);
symbolGroup.add(crText);
return symbolGroup;
}
make sure you do
layer.add(group);
and if you already are,
layer.draw();
to redraw the layer. also remove
opacity: 0.8,
clearBeforeDraw: true
as attributes of the layer.

Categories

Resources