我在d3图表上显示以下数据:
[{
"Date": "2012",
"Total": 5340
}, {
"Date": "2013",
"Total": 4120
}, {
"Date": "2014",
"Total": 1259
}, {
"Date": "2015",
"Total": 3617
}, {
"Date": "2016",
"Total": 2005
}]
图表上还有一个画笔,因此用户可以通过拖动画笔来专注于选择年份。但是,画笔允许用户在第一年和去年之间的所有日期拖动。
我想要做的就是在用户刷牙的时候使它快照多年。
这是刷子事件的代码:
var brush = d3.svg.brush()
.x(xScale2)
.on('brush', brushed);
function brushed() {
var min = d3.min(data.map(function (d) {
return d.total;
}));
var max = d3.max(data.map(function (d) {
return d.total;
}));
var extent = brush.extent();
if (!brush.empty()) {
// Snap to years
var newBrush = extent.map(d3.time.year.round);
if (newBrush[0] >= newBrush[1]) {// If empty when rounded, use floor & ceil instead.
newBrush[0] = d3.time.year.floor(extent[0]);
newBrush[1] = d3.time.year.ceil(extent[1]);
}
// update the extent
brush.extent([newBrush[0], newBrush[1]]);
extent = brush.extent();
xScale.domain(brush.empty() ? xScale2.domain() : extent);
yScale.domain([
d3.min(data.map(function (d) {
return (d.date >= extent[0] && d.date <= extent[1]) ? d.total : max;
})),
d3.max(data.map(function (d) {
return (d.date >= extent[0] && d.date <= extent[1]) ? d.total : min;
}))
]);
xScale.domain(brush.empty() ? xScale2.domain() : extent);
yScale.domain([
d3.min(data.map(function (d) {
return (d.date >= extent[0] && d.date <= extent[1]) ? d.total : max;
})),
d3.max(data.map(function (d) {
return (d.date >= extent[0] && d.date <= extent[1]) ? d.total : min;
}))
]);
}
totalChart.attr('d', totalLine); // update line
focus.select('.x.axis').call(xAxis); // update xAxis
focus.select('.y.axis').call(yAxis); // update yAxis
}
这基于:https://bl.ocks.org/mbostock/6232620
所以我的想法是先将年份四舍五入,然后在应用于域之前修改范围,并且应该使刷子快速移动...但是它不会捕捉...而是它会使用户争取在这些年间拖动它。我想在用户开始拖动时实际捕捉到下一年。他们应该永远不能拖延多年。
答案 0 :(得分:2)
这是带有年份捕捉的mbostock代码。它只是有一个背景界限的小bug。
除了背景/画笔元素同步中的一点缺陷外,此代码工作正常。
var margin = {top: 0, right: 40, bottom: 50, left: 40},
width = 500 - margin.left - margin.right,
height = 100 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([new Date(2012, 1, 1), new Date(2023, 1, 1)])
.range([0, width]);
var brush = d3.svg.brush()
.x(x)
.extent([new Date(2014, 1, 1), new Date(2015, 1, 1)])
.on("brushend", brushended);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("rect")
.attr("class", "grid-background")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months, 6)
.tickSize(-height)
.tickFormat(""))
.selectAll(".tick")
.classed("minor", function(d) { return d.getHours(); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.years)
.tickPadding(0))
.selectAll("text")
.attr("x", 6)
.style("text-anchor", null);
var gBrush = svg.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.event);
gBrush.selectAll("rect")
.attr("height", height);
function brushended() {
if (!d3.event.sourceEvent) return; // only transition after input
var extent0 = brush.extent(),
extent1 = extent0.map(d3.time.year.round);
// if empty when rounded, use floor & ceil instead
if (extent1[0] >= extent1[1]) {
extent1[0] = d3.time.year.floor(extent0[0]);
extent1[1] = d3.time.year.ceil(extent0[1]);
}
d3.select(this).transition()
.call(brush.extent(extent1))
.call(brush.event);
}
.axis text {
font: 11px sans-serif;
}
.axis path {
display: none;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.grid-background {
fill: #ddd;
}
.grid line,
.grid path {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
.grid .minor.tick line {
stroke-opacity: .5;
}
.brush .extent {
stroke: #000;
fill-opacity: .125;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<body>
</body>