Disable brush resize (DC.js, D3.js)
Asked Answered
C

2

6

Brush extent needs to be changed only from a dropdown as shown here: https://jsfiddle.net/dani2011/67jopfj8/3/

Need to disable brush extending by:

1) Extending an existing brush using the handles/resize-area of the brush

Gray circles are the handels:

enter image description here

2) Dragging a new brush by clicking on the brush background, where the haircross cursor appears.

JavaScript file

Removed the handles of the brush:

timeSlider.on('preRedraw',function (chart)
    {
        var timesliderSVG = d3.select("#bitrate-timeSlider-chart").selectAll("g.brush").selectAll("g.resize").selectAll("*").data(data[0]).exit().remove();})

If using css instead:

#bitrate-timeSlider-chart g.resize {
           display:none;
           visibility:hidden;

Now it looks like this:

enter image description here.

The rect and the path elements inside "resize e","resize w" were removed:

enter image description here

However,the "resize e", "resize w" for extanding the brush still exist:

enter image description here

g.resize.e and g.resize.w dimesions are 0*0:

enter image description here

Furthurmore,after deleting "resize e","resize w" in the "developer tools/elements" in chrome,they reappear after moving the brush.

Tried to remove the resize-area in brushstart,brush,brushend:

timeSlider.on('renderlet', function (chart) {
var brushg = d3.select("#bitrate-timeSlider-chart").selectAll("g.brush");
var resizeg = brushg.selectAll("g.resize").selectAll("*").data(data[0]);       
var timesliderSVG4 = 
brushg.on("brushstart", function () {resizeg.exit().remove()}).on("brush", function () { resizeg.exit().remove() }).on("brushend", function () {resizeg.exit().remove() })

dc.js file

Tried to change setHandlePaths,resizeHandlePath:

1) Remarked the _chart.setHandlePaths(gBrush):

_chart.renderBrush = function (g) {....
 //   _chart.setHandlePaths(gBrush);
 ...}

2) Changed _chart.setHandlePaths = function (gBrush) for example by removing the gbrush.path:

  //  gBrush.selectAll('.resize path').exit().remove();

3) Remarked/changed _chart.resizeHandlePath = function (d) {...}.

d3.js file

1) Remarked/changed resize such as:

mode: "move" //mode: "resize" ,

var resize = g.selectAll(".resize").data(resizes[0], d3_identity); 

Using resizes[0] disable the brush rendering on the background but still can re-extend the existing brush

2) Remarked/changed d3_svg_brushResizes

3) In d3.svg.brush = function():

a) Added .style("display","none"):

background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("display", "none").style("cursor", "none");

b) background.exit().remove(); The cursor now is "pointer" instead of "haircross" extending the brush to a full width

c) d3_svg_brushCursor disabled makes the whole brush disappear

4) Changed the pointer-events as specified here: https://developer.mozilla.org/en/docs/Web/CSS/pointer-events

5) console.log in different places to track the different brush events:

function d3_event_dragSuppress(node) {        
        console.log("here2 ");          
    }
    if (d3_event_dragSelect) {
        console.log("here3 d3_event_dragSelect");
        ...
    }
    return function (suppressClick) {
        console.log("suppressClick1");
        ...
            var off = function () {
                console.log("suppressClick2");
               ...
            w.on(click, function () {
                console.log("suppressClick3")
               ...    
    function d3_mousePoint(container, e) {
    console.log("d3_mousePoint1")
    ...
    if (svg.createSVGPoint) {
        console.log("createSVGPoint");
       ...
            if (window.scrollX || window.scrollY) {
                console.log("createSVGPoint1");
                svg = d3.select("body").append("svg").style({
                    ...

    function dragstart(id, position, subject, move, end) {
        console.log("dragstart")
       ...
            function moved() {
                console.log("moved ");

             console.log("transition1");
...
          if (d3.event.changedTouches) {
            console.log("brushstart1");
           ...
        } else {
            console.log("brushstart2");
           ..
        if (dragging) {
            console.log("dragging4");
           ....
            if (d3.event.keyCode == 32) {
                if (!dragging) {
                    console.log("notdragging1");
                    ...
        function brushmove() {
            console.log("brushmove");
            ...
            if (!dragging) {
                console.log("brushmove!dragging");
                if (d3.event.altKey) {
                    console.log("brushmove!dragging1");
                    ...
            if (resizingX && move1(point, x, 0)) {
                console.log("resizeXMove1");
              ...

            if (resizingY && move1(point, y, 1)) {
                console.log("resizeYMove1");
               ...
            if (moved) {
                console.log("moved");
                ...
        }
        function move1(point, scale, i) {
            if (dragging) {
                console.log("dragging1");
                ...
            if (dragging) {
                console.log("dragging2");
            ...
            } else {
                console.log("dragging10");
                ...
            if (extent[0] != min || extent[1] != max) {
                console.log("dragging11");
                if (i) console.log("dragging12"); yExtentDomain = null;    
                console.log("dragging13");            
        function brushend() {
            console.log("brushend");
        ...

The two changes that seemed to get closest to the needed result are in d3.js:

1) Using resizes[0] disables the brush rendering on the background but still can re-extend the existing brush

var resize = g.selectAll(".resize").data(resizes[0], d3_identity); 

2) Removing the brush's background changes the cursor to "pointer" instead of "haircross",extending the brush to a full width only when clicking on the graph

`background.exit().remove();`

Any help would be very appreciated!

Coverture answered 16/12, 2016 at 4:13 Comment(3)
Have you seen "Disable d3 brush resize"? Make sure to not only read the accepted answer, there is valueable information in most of the other answers as well.Rosyrot
For taking control of the background click have a look at Mike Bostock's Click-to-Recenter Brush. Instead of the centering, you could just cancel the event.Rosyrot
Thank you for your references. Code from "Click-to-Recenter Brush" inside the .on(renderlet or .on('preRedraw' didn't change the result. Also tried to cancel the event with d3.event.stopPropagation(). Furthermore, the suggestions from "disable d3 brush resize" link did not disable the resize option on the brush. Will update the code in my fiddle with the relevant code from these examples remarked.Coverture
J
1

This is from the accepted answer in Disable d3 brush resize, as suggested by @altocumulus. I didn't see a response from @Dani on this idea in particular, but thought I'd go ahead and try it, since I've seen other people try it in the past. (Probably on the dc.js users group.)

It looks a little twitchy, because d3.js will draw the brush at the new extent, and then a moment later we reset the extent to what we want, but functionally it seems to do what we want.

In dc.js the function that handles brush "rounding" is coordinateGridMixin.extendBrush:

_chart.extendBrush = function () {
    var extent = _brush.extent();
    if (_chart.round()) {
        extent[0] = extent.map(_chart.round())[0];
        extent[1] = extent.map(_chart.round())[1];

        _g.select('.brush')
            .call(_brush.extent(extent));
    }
    return extent;
};

Notice that it's following the same pattern as Lars' answer. Well, this is sort of like rounding, right? Let's override it.

First, let's store the current number of hours when it's set through the dropdown:

var graphSpan;
function addHours(amountHours) {
  graphSpan = amountHours;
  // ...

Next let's override coordinateGridMixin.extendBrush:

timeSlider.extendBrush = function() {
    var extent = timeSlider.brush().extent();
    if(graphSpan) {
        extent[1] = moment(extent[0]).add(graphSpan, 'hours');
    }
    return extent;
}

We just replace the function. If we needed to reuse the old implementation in our function, we could use dc.override.

If graphSpan has been set, we add that amount to the beginning to get the end. If it's not set, we allow the user to specify the brush width - you'd need to default the graphSpan and the select widget if you wanted that part to work automatically.

Well, let's admit it: it's very twitchy, but it works:

twitchy fixed brush extent

EDIT: Got rid of the twitch! The problem was that dc.js was setting the brush extent asynchronously after a little while (in response to the filter event). If we also set it during extentBrush then it never shows the wrong extent:

timeSlider.extendBrush = function() {
    var extent = timeSlider.brush().extent();
    if(graphSpan) {
      extent[1] = moment(extent[0]).add(graphSpan, 'hours');
      timeSlider.brush().extent(extent);
    }
    return extent;
}

nicer brush resize

https://jsfiddle.net/gordonwoodhull/xdo05chk/1/

Joettajoette answered 18/12, 2016 at 14:55 Comment(8)
Thank you very very much Gordon- for your time,clear explanations and work-I really appreciate your help in resolving this problem! DaniCoverture
Sure thing, Dani! These days I feel it's worth the effort to figure out and write up canonical answers for common questions like this one. And SO amplifies the power of those answers - I see less and less questions repeated.Joettajoette
One more question regarding the solution:When I drag the line charts, the size of the brush on the timeChart still changes. Tried to change this by applying the timeSlider.extendBrush inside the bitChart2.on('postRedraw', with and without dc.renderall(). Also tried to implement the same code in other events such as bitChart2.on('renderlet'. Futhurmore try to override _chart.redrawBrush inside.on('postRedraw' of bitchart2. Any help would be appreciatedCoverture
Can you do mouseZoomable(false) on the focus charts? I meant to try this in my answer but I forgot. They don't have brushes so the same code won't help. However a feature of focus / range charts is to update the brush on the range chart when the zoom or pan of the focus charts change, so that's probably the problem. I suppose pan but not zoom would make sense in this scenario.Joettajoette
mouseZoomable(false) disables the panning of the line charts by the user,the update of data in the table and the resize of the brush.Tried to affect the brush of the range chart from within the event of the focus chart. Looking now into pan but not zoom and disabling the brush change of the range chart by the focus chart.Thank you (:Coverture
I can't follow this in comments. Please open another question and state clearly what is the desired effect. Thanks!Joettajoette
Please find an update to the code which enables to continue the brush-extent from its current position on the range chart: #41255316Coverture
Oh, this wasn't a question, it was an update. Sorry I didn't understand that.Joettajoette
O
0

What worked for me:

in d3:

disable resize handles d3.selectAll('.brush>.handle').remove();

disable crosshair d3.selectAll('.brush>.overlay').remove();

or in css:

disable resize handles -

.handle {
    pointer-events: none;
}

disable crosshair -

.overlay {
    pointer-events: none;
}
Originally answered 19/1, 2019 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.