第一个问题:
对于单条形图,当我们" mouseover"一个栏,我们将获得我们选择的栏的值i
。例如,以下代码检测我们选择的x轴的哪个元素,并将其分配给名为selectedYear
的变量:
.on("mouseover", function (d, i) {
selectedYear = i;
update();
barChart.selectAll("text")
.attr("fill", "black");
d3.select(this).attr("fill", "orange");
})
然而,当它是下面的堆积条时,我怎么能给出#34; 2006"到selectedYear
和" SmartPhone"鼠标悬停功能中的seletedCategory
?
第二个问题:
当鼠标悬停在栏上时,如何在x轴上突出显示相应的文字? 在上图中,我如何制作文字" 2006"当2006栏的任何区块被鼠标悬停时突出显示?
以下是我的全部代码:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script src="d3.min.js" charset="UTF-8"></script>
<script>
var width = 700;
var height = 500;
var dataSet;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var dataSet1 = [
{ name: "PC" ,
sales: [ { year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }] },
{ name: "SmartPhone" ,
sales: [ { year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }] },
{ name: "Software" ,
sales: [ { year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }] }
];
var stack = d3.layout.stack()
.values(function(d){ return d.sales; })
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; });
var data = stack(dataSet1);
var padding = { left:50, right:100, top:30, bottom:30 };
var xRangeWidth = width - padding.left - padding.right;
var xScale = d3.scale.ordinal()
.domain(data[0].sales.map(function(d){ return d.year; }))
.rangeBands([0, xRangeWidth],0.3);
var maxProfit = d3.max(data[data.length-1].sales, function(d){
return d.y0 + d.y;
});
var yRangeWidth = height - padding.top - padding.bottom;
var yScale = d3.scale.linear()
.domain([0, maxProfit])
.range([0, yRangeWidth]);
var color = d3.scale.category10();
var groups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.style("fill",function(d,i){ return color(i); });
var rects = groups.selectAll("rect")
.data(function(d){ return d.sales; })
.enter()
.append("rect")
.attr("x",function(d){ return xScale(d.year); })
.attr("y",function(d){ return yRangeWidth - yScale( d.y0 + d.y ); })
.attr("width",60)
.attr("height",function(d){ return yScale(d.y); })
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("stroke","white")
.on("mouseover", function (d, i) {
groups.selectAll("rect")
.attr("stroke","white")
d3.select(this)
.attr("stroke", "black")
.attr("stroke-width", 3);
selectedYear = i;
})
.on("mouseout", function (d) {
// d3.select(this).attr("stroke", "white").attr("stroke-width", 0);
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
yScale.range([yRangeWidth, 0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yRangeWidth) + ")")
.call(yAxis);
var labHeight = 50;
var labRadius = 10;
var labelCircle = groups.append("circle")
.attr("cx",width - padding.right*0.98)
.attr("cy",function(d,i){ return padding.top * 2 + labHeight * i; })
.attr("r",labRadius);
var labelText = groups.append("text")
.attr("x",width - padding.right*0.8)
.attr("y",function(d,i){ return padding.top * 2 + labHeight * i; })
.attr("dy",labRadius/2)
.text(function(d){ return d.name; });
</script>
</body>
</html>
答案 0 :(得分:1)
一些想法......
var width = 600,
height = 200,
padding = { left:50, right:200, top:20, bottom:30},
xRangeWidth = width - padding.left - padding.right,
yRangeWidth = height - padding.top - padding.bottom;
var dataSet;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + [padding.left, padding.top] + ")");
var dataSet1 = [
{ name: "PC" ,
sales: [ { year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }] },
{ name: "SmartPhone" ,
sales: [ { year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }] },
{ name: "Software" ,
sales: [ { year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }] }
],
// experiment to demonstrate unsupported data structure //////////////////////////////
dataset2 = dataSet1.map(function(d){
return {
name: d.name,
sales: d.sales.reduce(function(s, o) {
return (s[o.year] = o.profit, s)
},{})
}
}),
_stack = d3.layout.stack()
.values(layer)
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; }),
dataRaw = _stack(dataset2).map(function(d){
return {
name: d.name,
sales: layer(d)
}
});
function layer(d){
return Object.keys(d.sales).map(function(y){
return {profit: d.sales[y],year:y}
});
}
// end experiment /////////////////////////////////////////////////////////////////////
var offsetSelect = d3.ui.select({
before: "svg",
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeWidth + "px"},
onUpdate: function() {
update(dataSet1)
},
data : ["wiggle", "zero", "expand", "silhouette"]
}),
orderSelect = d3.ui.select({
before: "svg",
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeWidth - 20 + "px"},
onUpdate: function() {
update(dataSet1)
},
data : ["inside-out", "default", "reverse"]
}),
stack = d3.layout.stack()
.values(function(d){ return d.sales; })
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; })
.out(function out(d, y0, y) {
d.p0 = y0;
d.y = y;
}
);
// apply a transform to map screen space to cartesian space
// this removes all confusion and mess when plotting data!
var plotArea = svg.append("g")
.attr(transplot(yRangeWidth))
.attr("class", "plotArea");
// x Axis
var xPadding = {inner: 0.1, outer: 0.3},
xScale = d3.scale.ordinal()
.rangeBands([0, xRangeWidth], xPadding.inner, xPadding.outer),
xAxis = d3Axis()
.scale(xScale)
.orient("bottom"),
gX = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + yRangeWidth + ")");
// y Axis
var yAxisScale = d3.scale.linear()
.range([yRangeWidth, 0]),
yAxis = d3Axis()
.scale(yAxisScale)
.orient("left"),
gY = svg.append("g")
.attr("class", "y axis")
.style("pointer-events", "none");
var yScale = d3.scale.linear()
.range([0, yRangeWidth]);
var color = d3.scale.category10();
function update(dataSet) {
var data = stack.offset(offsetSelect.value())
.order(orderSelect.value())(dataSet),
maxProfit = d3.max(data,function(d) {
return d3.max(d.sales, function(s) {
return s.profit + s.p0
})
});
function yDomain(){return [0, offsetSelect.value() == "expand" ? 1 : maxProfit]}
xScale.domain(data[0].sales.map(stack.x()));
yAxisScale.domain(yDomain());
yScale.domain(yAxisScale.domain());
var series = plotArea.selectAll(".series")
.data(data);
series.enter()
.append("g")
.attr("class", "series");
series.style("fill", function(d, i) {
return color(i);
});
series.exit().remove();
var s = xScale.rangeBand(),
w = s - xPadding.inner,
points = series.selectAll("rect")
.data(function(d) {
return d.sales;
}),
newPoints = points.enter()
.append("rect")
.attr("width", w)
.on("mouseover", function(d) {
var rect = d3.select(this), selectedYear = d.year;
// if the plot is not normalised, offset the axis to align with the selected group
if(offsetSelect.value() != "expand") {
var pMin = d3.min(data,function(d) {
return d3.min(d.sales.filter(function(p) {
return p.year == selectedYear
}), function(s) {
return s.p0
})
});
yAxisScale.domain([-pMin, yDomain()[1] - pMin]);
gY.transition().call(yAxis).attr("transform", "translate(" + rect.attr("x") + ",0)");
}
// manage the highlighting
series.selectAll("rect")
.transition()
.attr({opacity: 0.5});
rect
.transition()
.attr({opacity: 1});
d3.selectAll(".x.axis .tick")
.filter(function(d) {
return d == selectedYear
})
.classed("highlight", true);
// move the selected element to the front
d3.select(this.parentNode)
.moveToFront();
gX.moveToFront();
// add the value for the moused over item to the legend text and highlight it
var g = d3.select(this.parentNode).selectAll(".label").select("text");
g.classed("highlight", true);
g.text(g.text() + ": " + d3.format(">8.0f")(d.profit));
series
.append("g")
.attr("class", "tooltip")
.attr("transform", "translate(" + [rect.attr("x"), rect.attr("y")] + ")")
.append("text")
.attr(transflip())
.text(d3.format(">8.0f")(d.profit))
.attr({x: "1em", y: -rect.attr("height")/2, dy: ".35em", opacity: 0})
.transition().attr("opacity", 1)
.style({fill: "black", "pointer-events": "none"})
})
.on("mouseout", function(d) {
var year = d.year;
d3.selectAll(".x.axis .tick")
.filter(function(d) {
return d == year
})
.classed("highlight", false);
series.selectAll("rect")
.transition()
.attr({opacity: 1});
var g = d3.select(this.parentNode).select("text");
g.classed("highlight", false);
g.text(g.text().split(":")[0])
yAxisScale.domain(yDomain());
yAxis.tickSize(6);
gY.transition().call(yAxis).attr("transform", "translate(0,0)");
series.selectAll(".tooltip")
.transition()
.attr({opacity: 0})
.remove();
});
points.transition()
.attr("x", function(d) {
return xScale(d.year);
})
.attr("y", function(d) {
return yScale(d.p0);
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("stroke", "white");
points.exit().remove;
gX.transition().call(xAxis);
gY.transition().call(yAxis);
// Add the legend inside the series containers
// The series legend is wrapped in another g so that the
// plot transform can be reversed. Otherwise the text would be mirrored
var labHeight = 40,
labRadius = 10,
label = series.selectAll(".label").data(function(d){return [d.name]}),
newLabel = label.enter().append("g")
.attr("class", "label")
// reverse the transform (it is it's own inverse)
.attr(transplot(yRangeWidth));
label.exit().remove();
// add the marker and the legend text to the normalised container
// push the data (name) down to them
var labelCircle = label.selectAll("circle").data(aID),
// take a moment to get the series order delivered by stack
orders = data.map(function(d) { // simplify the form
return {name: d.name, base: d.sales[0].p0}
}).map(function(d) { // get a copy, sorted by p0
return d
}).sort(function(a, b){
return a.base - b.base
}).map(function(d) { // convert to index permutations
return data.map(function(p) {
return p.name
}).indexOf(d.name)
}).reverse(); // convert to screen y ordinate
labelCircle.enter().append("circle");
labelCircle.attr("cx", xRangeWidth + 20)
.attr("cy", function(d, i, j) {
return labHeight * orders[j];
})
.attr("r", labRadius);
var labelText = label.selectAll("text").data(aID);
labelText.enter().append("text");
labelText.attr("x", xRangeWidth + 40)
.attr("y", function(d, i, j) {
return labHeight * orders[j];
})
.attr("dy", labRadius / 2)
.text(function(d) {
return d;
});
function aID(d){
return [d];
}
}
update(dataSet1);
d3.selection.prototype.moveToFront = function() {
return this.each(function() {
this.parentNode.appendChild(this);
});
};
body {
position: relative;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis .tick line {
stroke: #ccc;
/*opacity: 0.5;*/
pointer-events: none;
}
.highlight {
font-weight: bold ;
}
svg {
overflow: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/inputs/select/select.js"></script>
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/plot/plot-transform.js"></script>