Fabric.js Ungroup items change their positions - javascript

Hi I'm trying to ungroup items using fabric.js, but everytime I try to ungroup them their position changes on the canvas.
I tried many technique and even tried to manually recalculate the positions but I was always off.
What seams to be the best technique is listed on this post
https://gist.github.com/msievers/6069778
Since the _restoreObjectsState is called by the group.destroy(), I used this instead (both gave me the same results)
I have a jsfiddle that shows that shows the weird behavior.
http://jsfiddle.net/vq3Lj0th/
var canvas = new fabric.Canvas('viewport');
var text1 = new fabric.IText('Text1', {
left: 0,
top: 0
});
var text2 = new fabric.IText('Text2', {
left: 0,
top: 125
});
var group = new fabric.Group([text1, text2], {
left: 150,
top: 100
});
canvas.add(group);
document.getElementById('ungroup').addEventListener('click' ,function ungroup() {
var destroyedGroup = group.destroy();
var items = destroyedGroup.getObjects();
items.forEach(function (item) {
canvas.add(item);
});
canvas.remove(group);
});
as a reference, what i'm trying to do is to ungroup the items and send them to another canvas to simulate group edition mode. i'm almost there but I can't figure how to ungroup things without losing their position.
Thanks for your help.

I found a way to make it work:
The bleeding edge version of fabric resolve this issue:
https://github.com/kangax/fabric.js/issues/1798

Related

pts.js: Speed up animation of grid cells?

I'm using Pts.js to create a grid of cells and then color these cells depending on their distance to the mouse pointer. My code is largely based on a demo from the official Pts.js Website.
Pts.quickStart("#pt", "#123");
//
let pts = [];
var follower = new Pt();
space.add({
start: (bound) => {
pts = Create.gridCells(space.innerBound, 40, 20);
follower = space.center;
},
//
animate: (time, ftime) => {
follower = follower.add(space.pointer.$subtract(follower).divide(20));
form.stroke("#123");
//
let rects = pts.map((p) => {
let color;
let mag = follower.$subtract(Rectangle.center(p)).magnitude();
let r = Rectangle.fromCenter(Rectangle.center(p), Rectangle.size(p));
//
if (mag >= 100) {
color = "#000"
} else {
color = "#f00"
}
//
form.fill(color).rect(r);
})
//
form.fillOnly("#fff").point(space.pointer, 10, "circle");
}
})
//
space.bindMouse().bindTouch().play();
#pt {
width: 800px;
height: 600px;
margin: 30px auto 0;
}
<script src="https://unpkg.com/pts#0.9.4/dist/pts.min.js"></script>
<div id="pt"></div>
The implementation works absolutely fine. But I'd like to increase the speed with which the »coloring« of the cells »follows« the cursor, i.e. reduce the delay with which the red space around the cursor is animated. Ideally, I'd like to have no delay.
I'm new to Pts.js, so still wrapping my head around the docs, and I can't find an option or explanation for how to control the animation's speed. If anyone could point me to what I need to do here, that'd be great.
It seems that this line is what controls the behavior of the red grid area:
follower = follower.add(space.pointer.$subtract(follower).divide(20));
The value supplied to .divide() controls the speed at which the red area follows the cursor. Changing its argument from 20 to 1 (or even removing .divide(20) entirely) causes the "following" behavior to be immediate.
(Though, if you intend to remove the capability for that behavior, I suspect that entire line could be simplified.)

Tooltips displayed for all charts when hovering over a point on a chart

I'm making charts using node.js and Chartist.js where I'm creating tooltips for the points using jQuery and CSS.
Now, the problem is the chart is being generated in the server and will always have the same root class ct-chart and same other classes as they are being generated by Chartist.js. Since I have multiple charts with my code, when I hover over a point on one chart it displays tooltips over all the charts rather than just that chart itself.
How can I fix this?
Here is my code:
var $tooltip = $('<div class="tooltip tooltip-hidden"></div>').appendTo($('.ct-chart'));
console.log($tooltip);
$(document).on('mouseenter', '.ct-point', function() {
var seriesName = $(this).closest('.ct-point').attr('ct:meta'),
value = $(this).attr('ct:value');
$tooltip.text(seriesName);
$tooltip.removeClass('tooltip-hidden');
});
$(document).on('mouseleave', '.ct-point', function() {
$tooltip.addClass('tooltip-hidden');
});
$(document).on('mousemove', '.ct-point', function(event) {
$tooltip.css({
left: event.offsetX - $tooltip.width() / 2,
top: event.offsetY - $tooltip.height() - 20
});
});
I think what's happening is that $tooltip variable contains a jquery array of all of your charts. So when you do $tooltip.removeClass('tooltip-hidden'); it is removing the class from all of them.
You need a way to distinguish between them - I'd suggest something like:
$(document).on('mouseenter', '.ct-point', function() {
var seriesName = $(this).closest('.ct-point').attr('ct:meta'),
value = $(this).attr('ct:value'),
index = $(this).index(); // get the index of the point
$tooltip.eq(index).text(seriesName);
$tooltip.eq(index).removeClass('tooltip-hidden'); // use the index to remove only one class
});
I haven't tested this, but I think it should work. You'll have to apply this across all of your code.
I built this little test to show that when you use "appendTo", that variable gets populated with all of those elements you targeted. https://jsfiddle.net/wvLy2enm/
You should see this in your console log as well.

Responsive joint js diagram

Im using joint js library to create a diagram inside html, but i need it to be responsive as mi site.Thing is, i have it inside a div with a java class that open down and closes up with this code :
$('.open-down-up').click(function(){
var nameMore = $(this).attr("name");
var valMore = $(this).attr("value");
if(valMore == 0){
$(this).find('span').empty();
$(this).find('span').append("▼");
$('div[id='+nameMore+']').slideDown("normal");
$(this).attr("value","1");
}else if(valMore == 1){
$(this).find('span').empty();
$(this).find('span').append("►");
var n = nameMore.indexOf("process_process");
$('div[id='+nameMore+']').slideUp("normal", function(){
var n = nameMore.indexOf("process_process");
if(n > -1){
hide_panel_all();
}
});
$(this).attr("value","0");
}
});
SO, i already tried things like :
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
el: $('#modelCanvas'),
gridSize: 10,
height: $('#modelCanvas').height(),
width: $('#modelCanvas').width(),
gridSize: 1,
model: graph,
});
But it doesn't work...any idea or approach i can apply ??
Thanks in advance.
I found a way so it may be helpfull for someone who need it (for a resizing responsive):
It's necessary to scale the entire paper along with the window, so, we create a javascript event on it:
window.onresize = function(event) {
paper.setDimensions($('#your_div_id').width());
paper.scaleContentToFit({minScaleX: 0.3, minScaleY: 0.3, maxScaleX: 1 , maxScaleY: 1});
};
Using joint js properties whe resize the canvas along with the div based on that event but only affecting width, then set a max scale for X and Y axis. You can of course, adapt it of make conditions on it as you may need.
Hope it helps as for me !

FabricJS - Group selection brings objects to front while selected

When I select a group of objects all them are automatically rendered on top of everything. This is a annoying behaviour that makes hard to select other elements that were on top of the selected elements.
There is an example of that happening here.
function newRect(index) {
return new fabric.Rect({
width: 100,
height: 100,
top: index * 30,
left: index * 30,
fill: '#' + (0x1000000 + (Math.random()) * 0xffffff).toString(16).substr(1, 6),
});
}
var canvas = new fabric.Canvas('canvas');
var rect0 = newRect(0);
canvas.add(rect0);
var rect1 = newRect(1);
canvas.add(rect1);
var rect2 = newRect(2);
canvas.add(rect2);
var group = new fabric.Group([rect0, rect1]);
canvas.setActiveGroup(group).renderAll();
setTimeout(function() {
canvas.discardActiveGroup().renderAll();
}, 5000);
When the two elements that are behind another are selected, they are rendered on top of the third one. But when they are deselected, (wait 5 seconds until the group is discarded) they are rendered properly behind the element on top.
Is there anyway to disable this behaviour? I would that the selected elements maintain there z order while selected and do not be on top of everything.
I think I've found the solution.
I need to put the add preserveObjectStacking: true option while initializing the canvas.
var canvas = new fabric.Canvas('canvas', {
preserveObjectStacking: true,
});
https://jsfiddle.net/fbgu7697/3/
It's working as I expected but it seems to have a weird bug. I'll open it on github.
UPDATE
The issue was already fixed in 1.6.1 version.
https://jsfiddle.net/1vawp9gu/

Add javascript to Wordpress loop with class selection

I would like to add category icons to a Wordpress page, each icon animated with snap.svg.
I added the div and inside an svg in the loop that prints the page (index.php). All divs are appearing with the right size of the svg, but blank.
The svg has a class that is targeted by the js file.
The js file is loaded and works fine by itself, but the animation appears only in the first div of that class, printed on each other as many times it is counted by the loop (how many posts there are on the actual page from that category).
I added "each()" and the beginning of the js, but is not allocating the animations on their proper places. I also tried to add double "each()" for the svg location and adding the snap object to svg too, but that was not working either.
I tried to add unique id to each svg with the post-id, but i could not pass the id from inside the loop to the js file. I went through many possible solutions I found here and else, but none were adaptable, because my php and js is too poor.
If you know how should I solve this, please answer me. Thank you!
// This is the js code (a little trimmed, because the path is long with many randoms, but everything else is there):
jQuery(document).ready(function(){
jQuery(".d-icon").each(function() {
var dicon = Snap(".d-icon");
var dfirepath = dicon.path("M250 377 C"+ ......+ z").attr({ id: "dfirepath", class: "dfire", fill: "none", });
function animpath(){ dfirepath.animate({ 'd':"M250 377 C"+(Math.floor(Math.random() * 20 + 271))+ .....+ z" }, 200, mina.linear);};
function setIntervalX(callback, delay, repetitions, complete) { var x = 0; var intervalID = window.setInterval(function () { callback(); if (++x === repetitions) { window.clearInterval(intervalID); complete();} }, delay); }
var dman = dicon.path("m136 ..... 0z").attr({ id: "dman", class:"dman", fill: "#222", transform: "r70", });
var dslip = dicon.path("m307 ..... 0z").attr({ id: "dslip", class:"dslip", fill: "#196ff1", transform:"s0 0"});
var dani1 = function() { dslip.animate({ transform: "s1 1"}, 500, dani2); }
var dani2 = function() { dman.animate({ transform: 'r0 ' + dman.getBBox().cx + ' ' + dman.getBBox(0).cy, opacity:"1" }, 500, dani3 ); }
var dani3 = function() { dslip.animate({ transform: "s0 0"}, 300); dman.animate({ transform: "s0 0"}, 300, dani4); }
var dani4 = function() { dfirepath.animate({fill: "#d62a2a"}, 30, dani5); }
var dani5 = function() { setIntervalX(animpath, 200, 10, dani6); }
var dani6 = function() { dfirepath.animate({fill: "#fff"}, 30); dman.animate({ transform: "s1 1"}, 100); }
dani1(); }); });
I guess your error is here:
var dicon = Snap(".d-icon");
You are passing a query selector to the Snap constructor, this means Snap always tries to get the first DOM element with that class, hence why you're getting the animations at the wrong place.
You can either correct that in two ways:
Declare width and height inside the constructor, for example var dicon = Snap(800, 600);
Since you are using jQuery you can access to the current element inside .each() with the $(this) keyword. Since you are using jQuery instead of the dollar you could use jQuery(this).
Please keep in mind this is a jQuery object and probably Snap will require a DOM object. In jQuery you can access the dom object by appending a [0] after the this keyword. If var dicon = Snap( jQuery(this) ); does not work you can try with var dicon = Snap( jQuery(this)[0] );
Additionally, you have several .attr({id : '...', in your code. I assume you are trying to associate to the paths an ID which are not unique. These should be relatively safe since they sit inside a SVG element and I don't see you are using those ID for future selection.
But if you have to select those at a later time I would suggest to append to these a numerical value so you wont have colliding ID names.

Categories