d3.js画笔填充颜色直方图
d3.js brush fill color histogram
我用 d3.js
创建了一些直方图。
我设法根据 brush
的位置更改 rect
的填充颜色。
但我想更改 rect
内的颜色。例如,如果 brush start
在 rect
的中间,我希望我的 rect
有两种颜色。
目前这就是我所拥有的:
这就是我想要的:
我看过一些像 Here 这样的例子。我是 d3 的新手,我试图理解代码。
我看到他们使用 clip-path
当他们没有画笔时肯定会隐藏背景栏,而当他们有画笔时会显示它们,具体取决于画笔的范围。
这是一个JS Bin
更新
我已经详细阅读了link中提供的代码。我发现他们没有创建 <rect>
元素来制作图表,但 barPath
如下所示:
function barPath(groups) {
var path = [],
i = -1,
n = groups.length,
d;
while (++i < n) {
d = groups[i];
path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
}
return path.join("");
}
但我什至不明白这个函数中发生了什么,如果没有其他方法可以做到这一点,我也不明白如何用这种方式打点。
我不会尝试绘制部分条形图(正如您的编辑所建议的那样),而是将条形图附加两次,一次在底部为灰色,然后在顶部为钢蓝色。然后你可以只对蓝色条应用一个剪辑路径,当它们被剪辑时你会看到下面的灰色。
完整代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
.charts {
padding: 10px 0;
}
.chart {
padding-left: 20px;
padding-top: 10px;
}
.axis text {
font: 10px sans-serif;
fill: black;
}
.chart text {
font: 10px sans-serif;
fill: black;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*dont display yAxis for categorical variable*/
#chart .y.axis g {
display: none;
}
/*Labels in categorical chart */
text#catTitle.catTitle {
font: 10px sans-serif;
fill: white;
}
/*Color for the brush */
.brush rect.extent {
fill: steelblue;
fill-opacity: .125;
}
/*Color for the brush resize path*/
.brush .resize path {
fill: #eee;
stroke: #666;
}
/*Color for the hidden object*/
.hidden {
fill: grey;
}
.bar {
fill: steelblue;
}
</style>
</head>
<body>
<svg class="chart" id="chart"></svg>
<script>
var data = [{
key: 1,
value: 37
}, {
key: 1.5,
value: 13
}, {
key: 2.5,
value: 1
}, {
key: 3,
value: 4
}, {
key: 3.5,
value: 14
}, {
key: 4,
value: 18
}, {
key: 4.5,
value: 21
}, {
key: 5,
value: 17
}, {
key: 5.5,
value: 16
}, {
key: 6,
value: 5
}, {
key: 6.5,
value: 4
}];
var margin = {
top: 10,
right: 41,
bottom: 42,
left: 10
};
var width = 400 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.value
})])
.range([height, 0]);
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.key;
}) + 1])
.rangeRound([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var chart = d3.select(".chart#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", 15 + "px");
chart.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var brush = d3.svg.brush()
.x(x)
.on("brush", brushed)
.on("brushend", brushend);
function brushend() {
if (brush.empty()){
chart.select("#clip>rect")
.attr("x", 0)
.attr("width", width);
}
}
function brushed() {
var e = brush.extent();
chart.select("#clip>rect")
.attr("x", x(e[0]))
.attr("width", x(e[1]) - x(e[0]));
}
chart.selectAll(".hidden")
.data(data)
.enter().append("rect")
.attr("class", "hidden")
.attr("x", function(d) {
return x(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x(0.5))
.style("stroke", "white")
.append("title")
.text(function(d) {
return d.key;
});
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("clip-path", "url(#clip)")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x(0.5))
.style("stroke", "white")
.append("title")
.text(function(d) {
return d.key;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("text") //Add chart title
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Petal Length");
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
chart.append("g")
.attr("class", "x brush")
.call(brush) //call the brush function, causing it to create the rectangles
.selectAll("rect") //select all the just-created rectangles
.attr("y", -6)
.attr("height", (height + margin.top)) //set their height
function resizePath(d) {
var e = +(d == "e"),
x = e ? 1 : -1,
y = height / 3;
return "M" + (.5 * x) + "," + y + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6) + "V" + (2 * y - 6) + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y) + "Z" + "M" + (2.5 * x) + "," + (y + 8) + "V" + (2 * y - 8) + "M" + (4.5 * x) + "," + (y + 8) + "V" + (2 * y - 8);
}
chart.selectAll(".resize").append("path").attr("d", resizePath);
</script>
</body>
</html>
对于希望将@Mark 的回答带到 v6 的任何人:
const data = [{
key: 1,
value: 37
}, {
key: 1.5,
value: 13
}, {
key: 2.5,
value: 1
}, {
key: 3,
value: 4
}, {
key: 3.5,
value: 14
}, {
key: 4,
value: 18
}, {
key: 4.5,
value: 21
}, {
key: 5,
value: 17
}, {
key: 5.5,
value: 16
}, {
key: 6,
value: 5
}, {
key: 6.5,
value: 4
}];
// svg sizes
const width = 400,
height = 200;
const m = 50;
const margin = {
top: m,
right: m,
bottom: m,
left: m,
};
const y = d3.scaleLinear()
.domain(d3.extent(data, d => d.value))
.range([height - margin.bottom, margin.top]);
const x = d3.scaleLinear()
.domain(d3.extent(data, d => d.key).map((v, i) => i==0 ? v - 1 : v + 1))
.rangeRound([margin.left, width - margin.right]);
const svg = d3.select('svg')
.attr('width', width)
.attr('height', height)
.attr('viewBox', `0 0 ${width} ${height}`)
const rects = svg.append('g').attr('class', 'rects');
const clips = svg.append('g').attr('class', 'clips');
svg.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append('g')
.attr('class', 'y-axis')
.style('display', 'none')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(y));
svg.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('x', margin.left)
.attr('y', margin.top)
.attr('width', width - margin.right)
.attr('height', height - margin.bottom);
const brush = d3.brushX()
.extent([
[x.range()[0], margin.top],
[x.range()[1], height - margin.bottom]
])
.on('brush', brushed)
.on('start', brushed)
.on('end', brushend);
function brushend(e) {
if (!e.selection || !e.selection.length) {
svg.select('#clip>rect')
.attr('x', margin.left)
.attr('width', width - margin.right);
}
}
function brushed(e) {
svg.select('#clip>rect')
.attr('x', e.selection[0])
.attr('width', e.selection[1] - e.selection[0]);
const selected = {
x0: x.invert(e.selection[0]),
x1: x.invert(e.selection[1]),
}
}
rects.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', d => x(d.key))
.attr('y', d => y(d.value))
.attr('height', d => height - y(d.value) - margin.bottom)
.attr('width', 20)
.style('stroke', 'white')
.style('fill', 'gray')
.append('title')
.text(d => d.key);
clips.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('clip-path', 'url(#clip)')
.attr('x', d => x(d.key))
.attr('y', d => y(d.value))
.attr('height', d => height - y(d.value) - margin.bottom)
.attr('width', 20)
.style('stroke', 'white')
.append('title')
.text(d => d.key);
svg.append('g')
.attr('class', 'x brush')
.call(brush) // initialize the brush
.selectAll('rect')
.attr('y', 0)
.attr('height', height)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg/>
我用 d3.js
创建了一些直方图。
我设法根据 brush
的位置更改 rect
的填充颜色。
但我想更改 rect
内的颜色。例如,如果 brush start
在 rect
的中间,我希望我的 rect
有两种颜色。
目前这就是我所拥有的:
这就是我想要的:
我看过一些像 Here 这样的例子。我是 d3 的新手,我试图理解代码。
我看到他们使用 clip-path
当他们没有画笔时肯定会隐藏背景栏,而当他们有画笔时会显示它们,具体取决于画笔的范围。
这是一个JS Bin
更新
我已经详细阅读了link中提供的代码。我发现他们没有创建 <rect>
元素来制作图表,但 barPath
如下所示:
function barPath(groups) {
var path = [],
i = -1,
n = groups.length,
d;
while (++i < n) {
d = groups[i];
path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
}
return path.join("");
}
但我什至不明白这个函数中发生了什么,如果没有其他方法可以做到这一点,我也不明白如何用这种方式打点。
我不会尝试绘制部分条形图(正如您的编辑所建议的那样),而是将条形图附加两次,一次在底部为灰色,然后在顶部为钢蓝色。然后你可以只对蓝色条应用一个剪辑路径,当它们被剪辑时你会看到下面的灰色。
完整代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
.charts {
padding: 10px 0;
}
.chart {
padding-left: 20px;
padding-top: 10px;
}
.axis text {
font: 10px sans-serif;
fill: black;
}
.chart text {
font: 10px sans-serif;
fill: black;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*dont display yAxis for categorical variable*/
#chart .y.axis g {
display: none;
}
/*Labels in categorical chart */
text#catTitle.catTitle {
font: 10px sans-serif;
fill: white;
}
/*Color for the brush */
.brush rect.extent {
fill: steelblue;
fill-opacity: .125;
}
/*Color for the brush resize path*/
.brush .resize path {
fill: #eee;
stroke: #666;
}
/*Color for the hidden object*/
.hidden {
fill: grey;
}
.bar {
fill: steelblue;
}
</style>
</head>
<body>
<svg class="chart" id="chart"></svg>
<script>
var data = [{
key: 1,
value: 37
}, {
key: 1.5,
value: 13
}, {
key: 2.5,
value: 1
}, {
key: 3,
value: 4
}, {
key: 3.5,
value: 14
}, {
key: 4,
value: 18
}, {
key: 4.5,
value: 21
}, {
key: 5,
value: 17
}, {
key: 5.5,
value: 16
}, {
key: 6,
value: 5
}, {
key: 6.5,
value: 4
}];
var margin = {
top: 10,
right: 41,
bottom: 42,
left: 10
};
var width = 400 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.value
})])
.range([height, 0]);
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.key;
}) + 1])
.rangeRound([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var chart = d3.select(".chart#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", 15 + "px");
chart.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var brush = d3.svg.brush()
.x(x)
.on("brush", brushed)
.on("brushend", brushend);
function brushend() {
if (brush.empty()){
chart.select("#clip>rect")
.attr("x", 0)
.attr("width", width);
}
}
function brushed() {
var e = brush.extent();
chart.select("#clip>rect")
.attr("x", x(e[0]))
.attr("width", x(e[1]) - x(e[0]));
}
chart.selectAll(".hidden")
.data(data)
.enter().append("rect")
.attr("class", "hidden")
.attr("x", function(d) {
return x(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x(0.5))
.style("stroke", "white")
.append("title")
.text(function(d) {
return d.key;
});
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("clip-path", "url(#clip)")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x(0.5))
.style("stroke", "white")
.append("title")
.text(function(d) {
return d.key;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("text") //Add chart title
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Petal Length");
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
chart.append("g")
.attr("class", "x brush")
.call(brush) //call the brush function, causing it to create the rectangles
.selectAll("rect") //select all the just-created rectangles
.attr("y", -6)
.attr("height", (height + margin.top)) //set their height
function resizePath(d) {
var e = +(d == "e"),
x = e ? 1 : -1,
y = height / 3;
return "M" + (.5 * x) + "," + y + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6) + "V" + (2 * y - 6) + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y) + "Z" + "M" + (2.5 * x) + "," + (y + 8) + "V" + (2 * y - 8) + "M" + (4.5 * x) + "," + (y + 8) + "V" + (2 * y - 8);
}
chart.selectAll(".resize").append("path").attr("d", resizePath);
</script>
</body>
</html>
对于希望将@Mark 的回答带到 v6 的任何人:
const data = [{
key: 1,
value: 37
}, {
key: 1.5,
value: 13
}, {
key: 2.5,
value: 1
}, {
key: 3,
value: 4
}, {
key: 3.5,
value: 14
}, {
key: 4,
value: 18
}, {
key: 4.5,
value: 21
}, {
key: 5,
value: 17
}, {
key: 5.5,
value: 16
}, {
key: 6,
value: 5
}, {
key: 6.5,
value: 4
}];
// svg sizes
const width = 400,
height = 200;
const m = 50;
const margin = {
top: m,
right: m,
bottom: m,
left: m,
};
const y = d3.scaleLinear()
.domain(d3.extent(data, d => d.value))
.range([height - margin.bottom, margin.top]);
const x = d3.scaleLinear()
.domain(d3.extent(data, d => d.key).map((v, i) => i==0 ? v - 1 : v + 1))
.rangeRound([margin.left, width - margin.right]);
const svg = d3.select('svg')
.attr('width', width)
.attr('height', height)
.attr('viewBox', `0 0 ${width} ${height}`)
const rects = svg.append('g').attr('class', 'rects');
const clips = svg.append('g').attr('class', 'clips');
svg.append('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append('g')
.attr('class', 'y-axis')
.style('display', 'none')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(y));
svg.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('x', margin.left)
.attr('y', margin.top)
.attr('width', width - margin.right)
.attr('height', height - margin.bottom);
const brush = d3.brushX()
.extent([
[x.range()[0], margin.top],
[x.range()[1], height - margin.bottom]
])
.on('brush', brushed)
.on('start', brushed)
.on('end', brushend);
function brushend(e) {
if (!e.selection || !e.selection.length) {
svg.select('#clip>rect')
.attr('x', margin.left)
.attr('width', width - margin.right);
}
}
function brushed(e) {
svg.select('#clip>rect')
.attr('x', e.selection[0])
.attr('width', e.selection[1] - e.selection[0]);
const selected = {
x0: x.invert(e.selection[0]),
x1: x.invert(e.selection[1]),
}
}
rects.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', d => x(d.key))
.attr('y', d => y(d.value))
.attr('height', d => height - y(d.value) - margin.bottom)
.attr('width', 20)
.style('stroke', 'white')
.style('fill', 'gray')
.append('title')
.text(d => d.key);
clips.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('clip-path', 'url(#clip)')
.attr('x', d => x(d.key))
.attr('y', d => y(d.value))
.attr('height', d => height - y(d.value) - margin.bottom)
.attr('width', 20)
.style('stroke', 'white')
.append('title')
.text(d => d.key);
svg.append('g')
.attr('class', 'x brush')
.call(brush) // initialize the brush
.selectAll('rect')
.attr('y', 0)
.attr('height', height)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg/>