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.
Related
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();
I am making charts using c3.js. I have to make the contents of the tooltip cilckable. Till now, the tooltip is visible only when i hover over the chart. I have some Information which is to be displayed when i click on a link in the tooltip. I couldn't find any help from c3 documentation. Snippet of the code i am working on is shown below.
$scope.timelineConfig.tooltip.contents = function (data, defaultTitleFormat, defaultValueFormat, color) {
var $$ = this, config = $$.config,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value;
text = "<div id='tooltip' class='d3-tip'>";
title = dates[data[0].index];
text += "<span class='info'><b><u>Date</u></b></span><br>";
text += "<span class='info'>"+ title +"</span><br>";
text += "<span class='info'><b><u>Features</u> : </b> " + features[data[0].index] + "</span><br>";
text += "<span class='info'><b><u>Enhancements</u> : </b> " + defects[data[0].index] + "</span><br>";
text += "</div>";
return text;
};
I have to make the contents (<span><b><u>Features...</u></b></span>) clickable.
First (if you haven't already done so) override the tooltip position so that it doesn't keep running away when you try to click it.
tooltip: {
position: function () {
var position = c3.chart.internal.fn.tooltipPosition.apply(this, arguments);
position.top = 0;
return position;
},
Then you need to override the hideTooltip function so that it doesn't close before your click event can be detected.
var originalHideTooltip = chart.internal.hideTooltip
chart.internal.hideTooltip = function () {
setTimeout(originalHideTooltip, 100)
};
Then, you just need to override the pointer-events style (so that the mouse events are not ignored) and then attach the handler as you normally would in jQuery
$(".c3-tooltip-container")
.css("pointer-events", "auto")
.on('click', '.info:eq(2)', function () {
// add click functionality here. you could pass in additional data using the span attributes
alert($(this).text())
})
Modify the selector as required (like adding the chart wrapper id...)
Fiddle - http://jsfiddle.net/5vbeb4k8/
I know I'm commenting on an old question, but just for reference in case anyone else needs it, I modified the above answer to work for my code.
In my CSS:
.c3-tooltip-container {
pointer-events: auto !important;
}
In JS:
c3.chart.internal.fn.hideTooltip = function () {
setTimeout(c3.chart.internal.fn.hideTooltip, 100);
};
The position code seems to be optional. But the fixed top is probably more user-friendly.
tooltip: {
position: function () {
var position = c3.chart.internal.fn.tooltipPosition.apply(this, arguments);
position.top = 0;
return position;
},
Thanks #potatopeelings for getting me started with this -- it was a huge help.
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 have a float graph as shown in the figure. I need to highlight a label when a corresponding plot is clicked.
/*The below is my function where my HTML is defined in JavaScript*/
function insertlabel()
{
placeholder.find("ticklabels").remove();
var html=['<div class="ticklabels" style="font-size:smaller; color:'+options.grid.color+'">'];
function addlabels(axis,labelgenerate){
for(var i=0;i<axis.ticks.length;++i){
var tick=axis.ticks[i];
if(!tick.label||tick.v<axis.min||tick,v>axis.max)
continue;
html.push(labelgenerate(tick,axis));
}
}
}
//This is the addlabel function
addlabels(axes.yaxis,function(tick,axis){
return '<div id="text" style=position: absolute ; some calculation +ticklabel /div>'
}
/*The jquery code is as follows.*/
$(this).click(function() {
$('#text').each(function() {
$(this).css('color','#C4BD97')
});
});
This is the jquery code i tried but its highlighting only the last label whichever plot is clicked. I want to highlight only the corresponding label when its series plot is clicked.
Here's an example that'll highlight the y axis label of the y value of the point clicked. I didn't bother replacing flot's axis labels with custom labels, as you seem to be doing (I'm not sure where you are going with all that).
$("#placeholder").bind("plotclick", function (event, pos, item) {
if (item){
var yVal = item.datapoint[1];
var yAxis = plot.getAxes().yaxis;
for (var i = 0; i < yAxis.ticks.length; i++){
if (yAxis.ticks[i].v == yVal){
yCor = yAxis.p2c(yVal);
$('#arrow').css({'display':'block','top':yCor});
}
}
}else{
$('#arrow').css({'display':'none','top':yCor});
}
});
Fiddle here.
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.