Grouping and Ungrouping Fabric.js objects - javascript

I've created a kind of 'polygon selector' or 'polygon maker' using fabric.js. Each click creates a corner of the polygon, which can be selected, moved, etc... double clicking the original point 'closes' the polygon. At this point I take all of the circles/lines that make up the polygons and group them. So far so good.
When such a group is double clicked, I would like it to ungroup and revert to movable nodes (i.e. moving the circles reshapes the polygon etc); but there's some strangeness going on - check out what happens when you move the circles, certain lines seem 'not joined' to the circles...
I've already reviewed every group/ungroup related fabric.js thread (here/there/everywhere). None seem to cover the type of 'connected' objects I have here.
The fiddle I put together to show the problem says it better than I can: http://jsfiddle.net/bhilleli/4v8mkw6q/
The broken bit of code is #:
//dbl clicked a group so lets ungroup it!
items = p._objects; // grab the items from the group you want to
// translate the group-relative coordinates to canvas relative ones
p._restoreObjectsState();
// remove the original group and add all items back to the canvas
canvas.remove(p);
for (var i = items.length - 1; i >= 0; i--) {
canvas.add(items[i]);
}
canvas.renderAll();

you can use fabric grouping tool
You can group and ungroup objects together, and manipulate them at the
same time
for example
var canvas = new fabric.Canvas('paper',{
isDrawingMode: true
});
$("#select").click(function(){
canvas.isDrawingMode = false;
});
$("#draw").click(function(){
canvas.isDrawingMode = true;
});
$("#group").on('click', function() {
var activegroup = canvas.getActiveGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
});
});
$("#ungroup").click(function(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
alert(items);
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
canvas.item(canvas.size()-1).hasControls = true;
}
canvas.renderAll();
}
});
please check following link
http://jsfiddle.net/softvar/NuE78/1/

if (!canvas.getActiveObject()) {
return;
}
if (canvas.getActiveObject().type !== 'group') {
return;
}
canvas.getActiveObject().toActiveSelection();
canvas.requestRenderAll();
From : http://fabricjs.com/manage-selection

if getActiveGroup() is not available then you can use this to group (after mouse selecting multiple objects):
toGroup() is only available if multiple objects are selected
var activeObj = canvas.getActiveObject();
var activegroup = activeObj.toGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.remove(activegroup);
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
});
changes
http://fabricjs.com/v2-breaking-changes-2

Related

Layer switcher for OpenLayers3 (bindto replacement)

In order to generate checkbox from layers array, I am using this example: https://openlayersbook.github.io/ch04-interacting-with-raster-data-source/example-06.html. However, since this part of the code:
var visible = new ol.dom.Input(document.getElementById('layer_id_' + i));
visible.bindTo('checked', layers[i], 'visible');
is deprecated since v3.5.0 (I am using v3.8.2), I found out that I can change it with this one:
var layer = new ol.layer.Tile();
var checkbox = document.querySelector('#checkbox');
checkbox.addEventListener('change', function() {
var checked = this.checked;
if (checked !== layer.getVisible()) {
layer.setVisible(checked);
}
});
layer.on('change:visible', function() {
var visible = this.getVisible();
if (visible !== checkbox.checked) {
checkbox.checked = visible;
}
});
but as I am JS noob, I was wondering if someone can help me out to implement this valid code in an example above + in addition to that I would like that only one layer, for example USA layer from Geoserver WMS demo, is checked and visible on load, and others are unchecked and not visible. (I don't know how to combain this events inside "for" loop - got stuck with it)

How to add a CSS property to selected nodes on sigma.js (canvas renderer)

I have created a functions that, when I click on a node, makes all non-neighboring nodes transparent*. Now I would like to make the same nodes unresponsive to mouse events, while keeping visible nodes responsive.
One option would be to assign the css attribute pointer-events:none to transparent nodes. Can I do this with sigma?
*To do so I assign an rgba color with opacity 0. Hence I must use the canvas renderer because WebGL does not support transparency.
My code:
function highlight () {
var s = sigma.instances()[0];
var nodes = s.graph.nodes();
var edges = s.graph.edges();
var maxCollab = d3.max(edges, function(d) { return d.collaborations; });
// We first need to save the original colors of our
// nodes and edges, like this:
nodes.forEach(function(n) {
n.originalColor = n.color;
});
edges.forEach(function(e) {
e.originalColor = e.color;
});
// When a node is clicked, we check for each node
// if it is a neighbor of the clicked one. If not,
// we set its color as grey, and else, it takes its
// original color.
// We do the same for the edges, and we only keep
// edges that have both extremities colored.
s.bind('clickNode', function(e) {
var nodeId = e.data.node.id,
toKeep = s.graph.neighbors(nodeId);
toKeep[nodeId] = e.data.node;
nodes.forEach(function(n) {
if (toKeep[n.id])
n.color = n.originalColor;
else
n.color = 'rgba(0,0,0,0)';
});
edges.forEach(function(e) {
if (toKeep[e.source] && toKeep[e.target]) {
e.color = e.originalColor;
}
else
e.color = 'rgba(0,0,0,0)';
});
// Since the data has been modified, we need to
// call the refresh method to make the colors
// update effective.
s.refresh();
});
// When the stage is clicked, we just color each
// node and edge with its original color.
s.bind('clickStage', function(e) {
nodes.forEach(function(n) {
n.color = n.originalColor;
});
edges.forEach(function(e) {
e.color = e.originalColor;
});
s.refresh();
});
}
Do you just want to hide the nodes? If so, you could set the hidden property of the nodes to true. This way they wouldn't be visible anymore and sigma wouldn't fire any events for them.
You can simply add a flag onto the node(s) meant to be unresponsive to the clickNode event.
// excerpt from `clickNode` handler
nodes.forEach(function(n) {
if (toKeep[n.id]) {
n.color = n.originalColor;
n.clickable = false; // <-- add this
} else {
n.color = 'rgba(0,0,0,0)';
}
});
Then only let the contents of the clickNode handler apply to those nodes.
// `clickNode` handler
s.bind('clickNode', function(e) {
if (e.data.node.clickable) {
// business as usual
}
});
Don't forget to set the flag to true in your clickStage handler.

highcharts drag and drop event binding

I have a highchart with two plotlines and one plotband extending between these two plotlines. I have added drag and drop events to all three (thanks to http://jsfiddle.net/48awM/30/, found via another answer here).
When I drag and drop lines A and B, I want the plotband to be updated such that it still extends between the new positions of A and B. If I do this by removing and adding plotband or updating the axis, I am not able to drag and drop the plotband anymore, the event is not bound to it any longer.
One thing I could do to overcome this is translating the plotband element, but I am not sure how exactly should I mention the translation parameters - for some reason when the translation parameters are generated dynamically based on the drag and drop of plotlines, the translation doesn't happen as expected.
I was wondering if there is a way to remove and add plotband but still maintain the drag and drop ability. This would be particularly useful since I would like to be able to drag and drop the plotband and change the positions of A and B such that they are at the beginning and end of the plotband as well.
My code is as follows.
#Component({
selector: 'smooth',
directives: [CHART_DIRECTIVES],
styles: [`
chart {
display: block;
}
`],
template: `<chart [options]="options" (load)="load($event.context)"></chart>
`
})
export class AppSmoothComponent
{ options={
xAxis:{plotLines:[{value:3,color:'red',width:5,id:'A',label: {text:'A',rotation:0,x:-18,style:{color:'red',fontWeight:'bold'}}},
{value:5,color:'red',width:5,id:'B',label:{text:'B',rotation:0,x:+12,style:{color:'red',fontWeight:'bold'}}}],
plotBands:[{color:'green',from:3,to:5,id:'Band',label:'Band'}]
},
series:[{data:[[1,2],[3,4],[5,6],[7,8],[9,10]]}],
}
constructor(){}
draggablePlotLine(axis,plotlineID)
{
var clickX,clickY;
var getPlotLine = function(){
for(var i=0;i<axis.plotLinesAndBands.length;i++)
{
if(axis.plotLinesAndBands[i].id===plotlineID)
{
return axis.plotLinesAndBands[i]}
}
}
var givenPlotLine = function(plotlineID){
for(var i=0;i<axis.plotLinesAndBands.length;i++)
{
if(axis.plotLinesAndBands[i].id===plotlineID)
{
return axis.plotLinesAndBands[i]}
}
}
var getValue=function(){
var plotLine=getPlotLine();
var translation=plotLine.svgElem.translateX;
var new_value=axis.toValue(translation)-axis.toValue(0)+plotLine.options.value;
new_value=Math.max(axis.min,Math.min(axis.max,new_value));
return new_value;
}
var getLabel=function(){
var plotLine=getPlotLine();
var label=plotLine.options.label;
return label;
}
var drag_start = function(e){
$(document).bind({
'mousemove.line':drag_step,
'mouseup.line':drag_stop
})
var plotLine=getPlotLine();
clickX=e.pageX-plotLine.svgElem.translateX;
}
var drag_step = function (e) {
var plotLine = getPlotLine();
var label=plotLine.options.label;
var new_translation = e.pageX - clickX ;
var new_value;
if(plotlineID=='Band'){new_value=axis.toValue(new_translation) - axis.toValue(0);
new_value = Math.max(axis.min, Math.min(axis.max, new_value));
new_translation = axis.toPixels(new_value + axis.toValue(0));
}
else {
new_value = axis.toValue(new_translation) - axis.toValue(0) + plotLine.options.value;
new_translation = axis.toPixels(new_value + axis.toValue(0) - plotLine.options.value);}
plotLine.svgElem.translate(new_translation,0);
};
var drag_stop = function () {
$(document).unbind('.line');
var plotLine = getPlotLine();
var plotLineOptions = plotLine.options;
console.log(plotLineOptions);
var label=plotLine.label;
//Remove + Re-insert plot line
//Otherwise it gets messed up when chart is resized
if (plotLine.svgElem.hasOwnProperty('translateX')) {
if(plotlineID=='Band'){
axis.removePlotBand(plotLineOptions.id);
axis.addPlotBand(plotLineOptions);
}
else{
plotLineOptions.value = getValue()
axis.removePlotLine(plotLineOptions.id);
axis.addPlotLine(plotLineOptions);
console.log(axis.plotLinesAndBands[2]);
if(plotlineID=='A')
{var Boptions=givenPlotLine('B')
console.log(Boptions);
axis.removePlotBand('Band');
axis.addPlotBand({from:plotLineOptions.value,to:Boptions.options.value,id:'Band' ,color:'green'})}
else if(plotlineID=='B')
{console.log(plotLineOptions.value,axis.plotLinesAndBands[0].options.value)
var Aoptions=givenPlotLine('A')
axis.removePlotBand('Band');
axis.addPlotBand({from:Aoptions.options.value,to:plotLineOptions.value,id:'Band',color:'green'});
}
}
}
getPlotLine().svgElem
.css({'cursor': 'pointer'})
.translate(0, 0)
.on('mousedown', drag_start);
};
drag_stop();
}
load(instance) {
this.draggablePlotLine(instance.xAxis[0],'A');
this.draggablePlotLine(instance.xAxis[0],'B');
this.draggablePlotLine(instance.xAxis[0],'Band');
console.log('ready');
}
}
P.S: The drag and drop of plotband is not working correctly in the above code. Also this assumes that A is always to the left of B.

dimple js interactive legend - hide few legends items at time of loading

I am working on something similar to the this.
This visualization shows all items in the legend at the time of loading. What I am trying to do is that when the visualization loads there are only few items checked in the legend and also visible on the chart for example: Tyrell Corp, Stark Ind and Rekall. For rest of them, I should have the option to turn on/make visible.
This is only required at the time of loading. After that I want the legend to behave normally as is does in this example.
I think something needs to change in this part of the code:
// Get a unique list of Owner values to use when filtering
var filterValues = dimple.getUniqueValues(data, "Owner");
// Get all the rectangles from our now orphaned legend
myLegend.shapes.selectAll("rect")
// Add a click event to each rectangle
.on("click", function (e) {
// This indicates whether the item is already visible or not
var hide = false;
var newFilters = [];
// If the filters contain the clicked shape hide it
filterValues.forEach(function (f) {
if (f === e.aggField.slice(-1)[0]) {
hide = true;
} else {
newFilters.push(f);
}
});
// Hide the shape or show it
if (hide) {
d3.select(this).style("opacity", 0.2);
} else {
newFilters.push(e.aggField.slice(-1)[0]);
d3.select(this).style("opacity", 0.8);
}
// Update the filters
filterValues = newFilters;
// Filter the data
myChart.data = dimple.filterData(data, "Owner", filterValues);
// Passing a duration parameter makes the chart animate. Without
// it there is no transition
myChart.draw(800);
});
Replace this:
// Get a unique list of Owner values to use when filtering
var filterValues = dimple.getUniqueValues(data, "Owner");
with this:
var filterValues = [];
var shapes = myLegend.shapes[0];
//By default, have only three owners showing up and the rest faded.
for (var i=0; i < shapes.length; i++)
{
if (i < 3)
{
var filterValue = $("text", shapes[i]).text();
filterValues.push(filterValue);
}
else
{
var rect = $("rect", shapes[i]);
rect.css("opacity", 0.2);
}
}
// Filter the data and redraw the chart to show only for three owners.
myChart.data = dimple.filterData(data, "Owner", filterValues);
myChart.draw();

get Active Elements of canvas not working

how can i get active Object or Active Group in below Example ?? i am using this . in this fiddle i am trying to create curved text whenever text-box value is changed . i need to get active object of canvas to apply size and all changes but active group or element not working . how can i do this ?
Fiddle ::
http://jsfiddle.net/NHs8t/8/
Below snippts of fiffle::
var act = canvas.getActiveGroup();
var obj = canvas.getActiveObject();
if(!obj)
{
console.log('object not Selected');
}else
{
console.log('Object selected');
}
if (!canvas.getActiveGroup())
{
console.log('if part executed--object not selected');
curvedText[cur] = new CurvedText(canvas, {});
curvedText[cur].center();
curvedText[cur].setText($(this).val());
cur++;
} else {
console.log('Else part executed-object selected');
act.setText($(this).val());
}
getActiveGroup seems to be buggy. getActiveObject returns the active group though, so you can go with that. Problem is you won't have access to your setText method through the group, so you'll have to keep a reference to the CurvedText instance in the group:
function CurvedText( canvas, options ){
...
this.group.curvedText = this;
}
and then
var act = canvas.getActiveObject();
act.curvedText.setText($(this).val());
like I did here.

Categories