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();
Related
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.
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.
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
I need to set the plotOptions -> events -> legendItemClick but manually on button click.
I though it would be something like chart.plotOptions.events.legendItemClick = function() {...}; but that obviously wasn't the solution.
I am not even sure if this is possible and I have to implement it on chart creation ONLY.
Any guidance on this is much appreciated. Thanks.
Setting in highcharts via creation:
plotOptions: {
series: {
events: {
legendItemClick: function() {
// Do something
}
}
}
}
What I want to do... (Post creation)
var chart = $('#container').highcharts(); // Get the highcharts
// This doesn't work
chart.plotOptions.series.legendItemClick = function() { // Set the legendItemClick
// Do something
}
We do it but within the legendItemClick itself. This work for us as we only have 2 states. The first is the initial load where we let the chart built with default legend click actions (all series are visible, clicking on a series in the legend hides that series, clicking on that series again shows it). The other state is when a user has selected a display option in a ddl that removes all but one series as being visible and then the user can click on a legend item and only show that item. We do it like:
function(event) {
var e = document.getElementById('" & gModeElementId & "');
var strGraphMode = e.options[e.selectedIndex].value;
if (strGraphMode == 'single') {
var seriesIndex = this.index;
var serie = this.chart.series;
for (i = 0; i < serie.length; i++) {
if (serie[i].index == seriesIndex) {
serie[i].show();
var ctitle = this.chart.yAxis[0].axisTitle;
ctitle.attr({text: serie[i].name});
} else {
serie[i].hide();
}
}
return false;
}
}
We also have not found a way to actually modify an existing legendItemClick function.
I'm trying to disable a button, hide a select list & show some text once a button is clicked... because of how long the javascript can take I am using timeouts to prevent the browser locking & the browser ending it prematurely or presenting a warning... however the code I have doesn't seem to be hiding/disabling/showing the elements once the button is clicked.
Edit: I have confirmed that the elements ARE getting hidden & then reshown, however they are being reshown too early.... the javascript hasn't finished doing what it's doing & they are reshown almost instantly after they are hidden.
Edit 2: Fixed it by moving the code that reshows the select list etc from the "addCatsSICMain" function to the "addCatsSIC" function as so..
if (spot < cats.options.length) {
other code here...
} else {
reshow select list etc code here
}
Here is the code:
This first function is the one that is called once the button is clicked.
function addCatsSICMain() {
// Set elements
var addBtn = document.getElementById('add');
var cat_sel = document.getElementById('cat_sic_sel_wrapper');
var addWait = document.getElementById('addWait');
// Disable add button
addBtn.disabled = true;
// Hide selected list
cat_sel.style.display = 'none';
// Show waiting text
addWait.style.display = 'block';
// Use a timeout function so button can be hid/show when we want successfully & not on function completion
setTimeout(function(){
// Add selected cats
addCatsSIC(0);
// Reshow selected list, reenable add button & hide wwaiting text
addWait.style.display = 'none';
cat_sel.style.display = 'block';
addBtn.disabled = false;
}, 10);
}
function addCatsSIC(spot) {
// Set the search results box
var cats = document.getElementById('cat_sic_list');
// Set the selected categories list that we are adding to..
var sel_cats = document.getElementById('cat_sic_sel');
// Set selcted counter var
var sel_count = 0;
// Set category add failed var
var failed = 0;
// Set batch size for looping
var batchSize = 50;
// Still more to do?
if (spot < cats.options.length) {
// Loop through categories from the search results select box
for (var i = spot; i < spot + batchSize && i < cats.options.length; i++) {
// Check if the cat is selected
if (cats.options[i].selected == true) {
// Set this category's values to some variables
var cat_id = cats.options[i].getAttribute('value');
var cat_name = cats.options[i].text;
if (checkCatSICAdd(cat_id) === false) {
// Now we create the new element
var new_option = document.createElement('option');
// Add attribute
new_option.setAttribute('value',cat_id);
// Create text node
var new_text_node = document.createTextNode(cat_name);
// Append new text node to new option element we created
new_option.appendChild(new_text_node);
// Append new option tag to select list
sel_cats.appendChild(new_option);
} else {
failed++;
}
}
}
var nextBitOfWork = function() { addCatsSIC(spot + batchSize) };
// Hand control back to the browser so it can update the page & not timeout & then restart the function
setTimeout(nextBitOfWork, 50);
}
if (failed > 0) {
// Find out if more than 2 cats were selected
for (var i = 0; i < cats.options.length; i++) {
if (cats.options[i].selected == true) {
sel_count++;
}
if (sel_count == 2) {
break;
}
}
// Give them an alert they have added that category already
/*addedCatSICAlert(sel_count);*/
}
}
Any reason why you are not using jQuery for this. You can disable button, hide select box and show elements by doing the following
$('button').click(function() {
$(this).attr('disabled', 'disabled');
$('select').hide();
$('p').show();
})
check working example at http://jsfiddle.net/N697c/1/
Fixed it by moving the code that reshows the select list etc from the "addCatsSICMain" function to the "addCatsSIC" function as so..
if (spot < cats.options.length) {
other code here...
} else {
reshow select list etc code here...
}