我正在使用此示例在第 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>