D3js中的多个无碰撞刷(V4)

时间:2018-03-17 01:21:38

标签: d3.js brushes

我正在使用此示例在版本4中的D3js中执行多个碰撞笔刷:http://bl.ocks.org/jssolichin/54b4995bd68275691a23(版本3)。我想申请这个例子:https://bl.ocks.org/NGuernse/4c75a051154cbe08bf80cddefefae22a。我几乎做到了,但它不能更新刷子碰撞。我已经获得了停止画笔的边缘/边界,但它只是在经过一次交互之后才会以交互方式碰撞。

我只需要将画笔位置更新为交互式碰撞

这是我的代码:

// set dimensions and margin
var margin = {top:50, bottom:50, left:50, right:50};
var width = 960, height = 500;

// create date parser
// var parseDate = d3.timeParse('%b %Y');

// create scales
var xScale = d3.scaleTime()
.range([0, width]);

var xScale_sec_2 = d3.scaleLinear()
.domain([0, 60])
.rangeRound([0, width]);

var xScale_sec = d3.scaleTime()
.domain([new Date(2013, 7, 1,8,0,0), new Date(2013, 7, 1,8,1,0)])
.rangeRound([0, width]);

var xScale_sec_ = d3.scaleLinear()
.domain([0, width])
.rangeRound([0, 60]);

var yScale = d3.scaleLinear()
.range([height, 0]);

// Define line
// var priceLine = d3.line()
// .x(function(d) { return xScale(d.date); })
// .y(function(d) { return yScale(d.price); });

// create plot area
var svg = d3.select('#plot')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);

var plotArea = svg.append('g')
.attr("id", "chart")
.attr('transform',
'translate(' + margin.left + ',' + margin.top + ')');

// Generate a SVG group to keep brushes
var gBrushes = svg.append('g')
.attr("height", height)
.attr("width", width)
.attr("fill", "none")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "brushes");

// Object to store brush selections and scatter data
var mySelections = {};

// Keep a default 3 of max brushes allowed
var brushCount = 40;

// Keep the actual d3-brush functions and their IDs in a list:
var brushes = [];

// Add grid
svg.append("g")
.attr("class", "axis axis--grid")
.attr("transform", "translate(50," + (height+50) + ")")
.call(d3.axisBottom(xScale_sec)
.ticks(d3.timeSecond, 1)
.tickSize(-height)
.tickFormat(function() { return null; }))
.selectAll(".tick")
.classed("tick--minor");

// Add Axes
plotArea.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(xScale_sec)
.ticks(d3.timeSecond.every(2))
.tickPadding(5)
.tickFormat(function(d) {
min = d.getMinutes();
sec = d.getSeconds();
if(sec == 0 && min != 0){sec = 60;}
// console.log(min, sec);
return sec; }))
.attr("text-anchor", null)
.selectAll("text")
.attr("x", -3);

plotArea.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(yScale));

// /************ Event listener to update brush counts *************/
// document.getElementById('brushInput').addEventListener('change', function() {
// brushCount = this.value;
// updateBrushes();
// });

// var edge = [];
newBrush();
drawBrushes();

var updateBrushes = function () {

if( brushes.length > brushCount) {
    let i = brushes.length-1;

    while(i >= brushCount) {
        let tempID = "brush-" + brushes[i].id;

        // Delete selections
        delete mySelections[tempID];

        d3.select('#' + tempID).remove();
        brushes.pop();
        i--;
    }

    drawBrushes();
}

if(brushes.length === 0 && brushCount > 0) {
    newBrush();
    drawBrushes();
}
}
/************ End of update brush counts *************/

//return an array that contains the closest brush edge to the left and right
function getBrushesAround(brush, brushes){

var edge = [0, xScale_sec_(width)];

// console.log("\n");

brushes.forEach(function(otherBrush) {

// console.log(otherBrush[0], otherBrush[1]);

if( brush[0] != otherBrush[0] && brush[1] != otherBrush[1] ){


  if (brush[0] != null && otherBrush[1] <= brush[0]) {

    if (edge[0] != null && otherBrush[1] > edge[0] || edge[0] == null){

      // console.log("1");
      edge[0] = otherBrush[1]; }

  }else if ( brush[0] != null && otherBrush[0] > brush[0] ) {

    if (edge[1] != null && otherBrush[0] < edge[1] || edge[1] == null){

      // console.log("2");
      edge[1] = otherBrush[0];
    }

  }

}

});

return edge;
}

/******* Brush features *******/
function newBrush() {
console.log("new brush");
var brush = d3.brushX()
.extent([[0, 0], [width, height]])
.on("start", brushstart)
.on("brush", brushed)
.on("end", brushend)

brushes.push({id: brushes.length, brush: brush});

function brushstart() {
// Brush start here
};

function brushed() {

let selection = d3.event.selection.map(i => xScale_sec.invert(i));
mySelections[this.id] = {start: selection[0], end: selection[1]};

id_brush = this.id.split("-")[1];
brush_selected = d3.brushSelection( document.getElementById(this.id) );

if(brush_selected != null){
    brush_selected = [xScale_sec_(brush_selected[0]), xScale_sec_(brush_selected[1])];
}

//find out what surrounds this brush
var all_Brushes = [];
brushes.forEach(function(d, a){
    br_arr = d3.brushSelection( document.getElementById("brush-" + d.id) );
    if(br_arr != null){
      br_arr = ( [xScale_sec_(br_arr[0]), xScale_sec_(br_arr[1])] );
      all_Brushes.push(br_arr);
    }
});

//Make sure no collision

    //find out what surrounds this brush
  var edge = getBrushesAround(brush_selected, all_Brushes);

//if the current block gets brushed beyond the surrounding block, limit it so it does not go past
if (edge[1] != null && brush_selected[1] >= edge[1] ) {
    brush_selected[1] = edge[1];
    } else if (edge[0] != null && brush_selected[0] <= edge[0] ) {
    brush_selected[0] = edge[0];
}

// ******** Capture the edge, but doesn't update the collision ********** //
d3.select(this).call( brushes[id_brush].brush.extent([ [xScale_sec_2(edge[0]), 0], [ xScale_sec_2(edge[1]) , height ] ]) );
}

function brushend() {

  // Figure out if our latest brush has a selection
  var lastBrushID = brushes[brushes.length - 1].id;
  var lastBrush = document.getElementById('brush-' + lastBrushID);
  var selection = d3.brushSelection(lastBrush);

  // ---- Snap ----
  if (!d3.event.sourceEvent) return; // Only transition after input.
  if (!d3.event.selection) return; // Ignore empty selections.
  var d0 = d3.event.selection.map(i => xScale_sec.invert(i)),
      d1 = d0.map(d3.timeSecond.round);
  // If empty when rounded, use floor & ceil instead.
  if (d1[0] >= d1[1]) {
    d1[0] = d3.timeSecond.floor(d0[0]);
    d1[1] = d3.timeSecond.offset(d1[0]);

}

d3.select(this).transition().call(d3.event.target.move, d1.map(xScale_sec));
// ---- ----

// If it does, that means we need another one
if (brushes.length < brushCount && selection && selection[0] !== selection[1]) {
  newBrush();
}

console.log("Brushend")

// Always draw brushes
drawBrushes();
}

}

function drawBrushes() {

var brushSelection = gBrushes
.selectAll('.brush')
.data(brushes, function (d){return d.id});

// Set up new brushes
brushSelection.enter()
.insert("g", '.brush')
.attr('class', 'brush')
.attr('id', function(brush){ return "brush-" + brush.id; })
.each(function(brushObject) {
// call the brush
brushObject.brush(d3.select(this));
});

brushSelection
.each(function (brushObject){
d3.select(this)
.attr('class', 'brush')
.selectAll('.overlay')
.style('pointer-events', function() {
var brush = brushObject.brush;
if (brushObject.id === brushes.length-1 && brush !== undefined) {
return 'all';
} else {
return 'none';
}
});
})

}
body {
padding: 25px;
font: 12px Arial;
}

path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}

.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}

.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}

.selection {
fill: green;
stroke: black;
stroke-width: 1px;
stroke-dasharray: 3px 3px;
}

.selected {
fill: lime !important;
stroke: black;
}

.wrapper .btn-block {
width: 50%;
}

.btn {
margin: 0 auto;
}
<title>Multi-brush plot</title> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.js" integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
        <div class="col-xs-3" id="info">
            <h3>Multi-brush scatter plot</h3>
            <p>The plot uses the d3.brush() to create multiple brushes for selected data.</p>


            <div class="input-group">
                <span class="input-group-addon">No. Brushes:</span>
                <input id="brushInput" type="number" min="0" max="5" class="form-control" value="2" >
            </div>
            <br>

            <div class="wrapper">
                <button id="remove-brushes-btn" class="btn btn-danger btn-block"><i class="fa fa-times-circle"></i> Brushes</button>
                <button id="find-data-btn" class="btn btn-primary btn-block"><i class="fa fa-search"></i> Find Data</button>
                <button id="match-two-btn" class="btn btn-success btn-block"><i class="fa fa-question-circle"></i> Count Lines</button>
                <button id="disable-btn" class="btn btn-default btn-block"><i class="fa fa-toggle-on"></i> Brushes On</button>
            </div>

        </div>

        <div id="plot" class="col-xs-9"></div>
    </div>
</div>
<script type="text/javascript" src="./plot.js"></script>

0 个答案:

没有答案