D3 version 4.2.2
I have a chart where I want the user to be able to keep zooming in with brush selections. I want to clear or reset my brush within the end event so that the user does not see the old brush.
Current TypeScript code:
let brushGroup = this.svg.append('g')
.attr('class', 'brush');
let b = brushX()
.on('start', () => {
console.log('start brush');
brushGroup.style('opacity', 100);
})
.on('end', () => {
console.log('end brush', event.selection);
// remove and display none destroy the brush, so use opacity
brushGroup.style('opacity', 0);
this.updateDateRange(this.xScale.invert(event.selection[0]),
this.xScale.invert(event.selection[1]));
});
brushGroup.call(b);
console.log(brushGroup);
display: none prevents future brush start events
brushGroup.remove() also destroys the brush
opacity works, but the brush is still there (mouse cursor changes on hover)
So what is the correct way to clear or reset the brush?
Should I just move it to 0,0?
Should I destroy the old brush and create a new one every time?
selection.call(brush.move, null);
https://github.com/d3/d3-brush/issues/10
Related
I am using Techanjs to create updating candlestick chart, the example I am referring is => http://bl.ocks.org/andredumas/95f1f22130fb1a3a8181
This example doesn't support zoom so from another example => http://bl.ocks.org/andredumas/a48008ea8e2c832144db I copied the code for zoom. My chart is zooming now but now the issue I am facing is the zoom got reset to the original zoom level when the new candle gets added in the chart. current code
function zoomed() {
var rescaledY = d3.event.transform.rescaleY(y);
yAxis.scale(rescaledY);
candlestick.yScale(rescaledY);
Code I tried:
function zoomed() {
let t = d3.zoomTransform( svg.node());
var rescaledY = t.rescaleY(y);
yAxis.scale(rescaledY);
candlestick.yScale(rescaledY);
below line is present where I am reading the json
zoomableInit = x.zoomable().clamp(false).copy();
To freeze the zoom I tried to modify the rescaleY and rescaleX functions but didn't really understand the required changes. Please suggest.
Following my previous question Disable resize of brush on range chart from focus charts (dc.js, d3.js) - Solved and my previous fiddle,https://jsfiddle.net/dani2011/uzg48yk7/1/, still need to disable brush drawing on the range chart before selecting a scale from the dropdown and/or on page load (!isPostback):
a) When panning /translating the line of the focus charts (bitChart,bitChart2) the brush is displayed on the whole range of the range chart:
b) It is possible to drag the brush on the range chart
Tried to cancel the zoom event using event listeners as followed:
var anotherRoot = d3.select("div#bitrate-timeSlider-chart.dc-chart").select(".chart-body");
anotherRoot.on("mousedown", null)
anotherRoot.on("mousemove.zoom", null)
anotherRoot.on("dblclick", null)
anotherRoot.on("touchstart", null)
anotherRoot.on("wheel", null)
anotherRoot.on("mousewheel.zoom", null)
anotherRoot.on("MozMousePixelScroll.zoom", null)
Tried to use different SVG scopes instead of anotherRoot such as:
//option 1
var rootSvg = d3.select("#bitrate-timeSlider-chart svg brush")
//option 2
var brushSVG = d3.select("#bitrate-timeSlider-chart").select("g.brush").select("*");
//option 3
d3.select("#bitrate-timeSlider-chart").on("touchstart.zoom", null);
d3.select("#bitrate-timeSlider-chart").on("mouse.zoom",
null);
Tried to cancel the event listeners:
1) Directly in my js file
2) Within the range chart (timeSlider)
3) Within the range chart events such as .on(render...) , .on(postRedraw...)
4) Tried to remove the brush within .on(postRedraw...) and within (!isPostBack) using:
//JS file
function isPostBack() { //function to check if page is a postback-ed one
return document.getElementById('_ispostback').value == 'True';
}
//HTML file
....
</script>
<input type="hidden" id="_ispostback" value="<%=Page.IsPostBack.ToString()%>" />
</body>
</html>
d3.select("#bitrate-timeSlider-chart").selectAll("g.brush").selectAll("*").data(data[0]).exit().remove();
Any help would be appreciated.
Okay, the answer I provided to the previous question for fixing the brush size was broken by these lines:
document.getElementById("alert").style.display = "inline";
There's no #alert element, so it crashes every time. I've restored that to the way I wrote it and it's a little bit messy when you drag, but at least it locks the brush size.
As for the other parts, now we're (finally) getting into documented behavior. Yay!
It's not perfect, but you can enable the brush only when there is a scale selection. Just disable it at first:
timeSlider
.brushOn(false)
and then enable it with a render when a scale has been selected:
function addHours(amountHours) {
var showBrush = +amountHours !== 0;
if(timeSlider.brushOn() !== showBrush)
timeSlider.brushOn(showBrush)
.render();
The render is not great, we'd rather do a redraw, but apparently the chart will only look at .brushOn() on render. Something to look into in the future.
We can also disable the styles which make it look like it has a ordinal brush and wants to be clicked on, like this:
.dc-chart rect.bar {
cursor: default;
}
.dc-chart rect.bar:hover {
fill-opacity: 1;
}
As for preventing zoom on the focus charts, you just need to set .zoomScale():
bitChartGeneral
.zoomScale([1,1]);
This sets d3.zoom.scaleExtent, locking the zoom.
Here's the updated fiddle: https://jsfiddle.net/gordonwoodhull/dsfqeut8/5/
I'm using d3 behavior api to implement some drag and drop functionality on my chart. There're some circles on the chart, what I want to do is make the circles able to move around when user is dragging them.
But I don't know how to get the reference of the circle which is being dragged by user.
In below code, where and how to get the reference of the selection and the current mouse position?
var drag = d3.behavior.drag()
.on("dragstart", function(){
//do some drag start stuff...
console.log('drag start');
})
.on("drag", function(){
//hey we're dragging, let's update some stuff
})
.on("dragend", function(){
//we're done, end some stuff
console.log('drag end');
});
Somebody please help!
You get the current selection from
d3.selection(this)
If you want to get the current mouse position, use this code
d3.mouse(this)
That returns an array, where [0] returns the x position of the mouse, and [1] returns the y position.
I've created an NVD3 multiBarChart and placed it in a jQuery resizable container. When resizing the chart, each render incurs the same delay as when the chart is first drawn: staggered left-to-right delayed drawing of the bars. This looks cool when the chart is first drawn, but it's a nuisance when resizing the chart. I've experimented with nv.d3.css, reducing every delay to 0ms to no avail. Haven't yet inspected the NVD3 JS and am hoping not to need to.
Fiddle:
http://jsfiddle.net/a5Fnj/10/
var container = $("#mycontainer");
$(container[0]).resizable();
var svg = d3.select(container[0]).append("svg");
nv.addGraph(function () {
var chart = nv.models.multiBarChart();
chart.xAxis.tickFormat(d3.format(',f'));
chart.yAxis.tickFormat(d3.format(',.1f'));
d3.select(container[0]).select("svg")
.datum(exampleData())
.transition().duration(0).call(chart);
nv.utils.windowResize(chart.update);
this.stackedbar = chart;
});
function exampleData() {
return stream_layers(3, 10 + Math.random() * 100, .1).map(function (data, i) {
return {
key: 'Stream' + i,
values: data
};
});
}
As of NVD3 1.7.1 you can use the duration option:
chart.duration(0);
I used transitionDuration: -1 that worked for a stackedAreaChart.
Edit
This helped remove the transition when appending chart data, not the re-size issue, please check the comments below.
In the latest version (from github), you can set .transitionDuration():
chart.transitionDuration(0);
Edit: Even with this, some of the transitions/durations are hardcoded in the NVD3 source. The only way to get rid of those is to modify the source.
I'm working on some bar charts and need to update the chart values. The only way I've found to do this is to redraw the whole thing. Isn't there a way to simple update the bars? And if so what I'm really hoping to do is animate that change. Any suggestions?
http://jsfiddle.net/circlecube/MVwwq/
Here's what you want (updated Fiddle).
You were on the right track for creating a new bar chart. The only issue is, you don't want to "display" that bar chart, but you want to use its bars for animation. While this does generate a new graph which we later throw away (using remove()), it seems to be Raphael best practice.
function b_animate(){
//First, create a new bar chart
var c2 = bars.g.barchart(200, 0, 300, 400, [bdata], {stacked: false, colors:["#999","#333","#666"]});
//Then for each bar in our chart (c), animate to our new chart's path (c2)
$.each(c.bars[0], function(k, v) {
v.animate({ path: c2.bars[0][k].attr("path") }, 200);
v.value[0] = bdata[k][0];
});
//Now remove the new chart
c2.remove();
}
This is not complete, as we haven't animated the legends to match the new chart, but this technique applied to the labels should get you there. Basically, we need to re-map the hovers to show new labels (and remove the old labels).
Hopefully, this should work exactly like you hoped. Let me know if you have any issues. Enjoy!
I had to adapt the above code to get this to work with Raphaƫl 2.1.0 and g.Raphael 0.51 and JQuery 1.9.1:
function b_animate(){
var c2 = bars.barchart(10, 10, 500, 450, bdata, { colors:custom_colors});
$.each(c.bars, function(k, v) {
v.animate({ path: c2.bars[k][0].attr("path") }, 500);
v[0].value = bdata[k][0];
});
c2.remove();}
Hope this helps!